From 8f43b6c584076f1ab9b78bc42f248ec0d7e1110a Mon Sep 17 00:00:00 2001 From: Matt Batchelder Date: Sat, 21 Feb 2026 16:47:11 -0500 Subject: [PATCH] Sync: implement scroll-reveal animations for cards with responsive visibility adjustments --- theme/assets/css/main.css | 19 +++++++++++++++++++ theme/assets/js/main.js | 32 +++++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/theme/assets/css/main.css b/theme/assets/css/main.css index 2ec851e..7033a67 100644 --- a/theme/assets/css/main.css +++ b/theme/assets/css/main.css @@ -97,6 +97,17 @@ img, video { max-width: 100%; display: block; } a { color: inherit; text-decoration: none; transition: color var(--transition); } a:hover { color: var(--color-primary); } +/* ── Scroll-reveal animation classes ───────────────────────── */ +.scroll-hidden { + opacity: 0; + transform: translateY(24px); +} +.scroll-visible { + opacity: 1; + transform: translateY(0); + transition: opacity .5s ease, transform .5s ease; +} + /* Smooth theme transition */ body, .site-header, @@ -4128,6 +4139,14 @@ p:last-child { margin-bottom: 0; } 50% { transform: translateY(-50%) scale(1.08); } } +/* ── Responsive scale-down ── */ +@media (max-width: 900px) { + .cam-stage { transform: scale(0.85); transform-origin: center center; } +} +@media (max-width: 640px) { + .cam-stage { transform: scale(0.72); transform-origin: center center; } +} + /* ── Reduced-motion ── */ @media (prefers-reduced-motion: reduce) { .cam-subject--2, .cam-subject--3, diff --git a/theme/assets/js/main.js b/theme/assets/js/main.js index a5349fb..d536b0b 100644 --- a/theme/assets/js/main.js +++ b/theme/assets/js/main.js @@ -134,17 +134,31 @@ document.addEventListener('DOMContentLoaded', () => { const cards = document.querySelectorAll('.oribi-card, .feature-card, .industry-card, .pricing-card, .value-card, .platform-row'); if (cards.length && 'IntersectionObserver' in window && !window.matchMedia('(prefers-reduced-motion: reduce)').matches) { - cards.forEach(c => { c.style.opacity = '0'; c.style.transform = 'translateY(24px)'; c.style.transition = 'opacity .5s ease, transform .5s ease'; }); - const io = new IntersectionObserver((entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - entry.target.style.opacity = '1'; - entry.target.style.transform = 'translateY(0)'; - io.unobserve(entry.target); + cards.forEach(c => c.classList.add('scroll-hidden')); + /* Use rAF to ensure the class is applied before observing – avoids + Safari quirk where elements already in-viewport don't fire. */ + requestAnimationFrame(() => { + const io = new IntersectionObserver((entries) => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.classList.remove('scroll-hidden'); + entry.target.classList.add('scroll-visible'); + io.unobserve(entry.target); + } + }); + }, { threshold: 0.05, rootMargin: '0px 0px 80px 0px' }); + cards.forEach(c => io.observe(c)); + }); + /* Safety net: reveal any still-hidden elements after 4 s so content + is never permanently invisible (e.g. iOS Safari edge-cases). */ + setTimeout(() => { + cards.forEach(c => { + if (c.classList.contains('scroll-hidden')) { + c.classList.remove('scroll-hidden'); + c.classList.add('scroll-visible'); } }); - }, { threshold: 0.1, rootMargin: '0px 0px 60px 0px' }); - cards.forEach(c => io.observe(c)); + }, 4000); } });