From d120bec8ce6578779b2c4cdb75d789b322ce4197 Mon Sep 17 00:00:00 2001 From: Matt Batchelder Date: Sun, 22 Mar 2026 01:45:38 -0400 Subject: [PATCH] feat: Add ambient animations for feature icons and update scroll-delay logic for card entrance --- theme/assets/css/main.css | 40 ++++++++++++++++++++++++++++++++ theme/assets/js/demo-animator.js | 9 ++++--- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/theme/assets/css/main.css b/theme/assets/css/main.css index 6288c28..46f5d33 100644 --- a/theme/assets/css/main.css +++ b/theme/assets/css/main.css @@ -129,6 +129,46 @@ a:hover { color: var(--color-primary); } animation: icon-pop 0.4s ease-out backwards; } +/* ── Ambient feature-icon animations ────────────────────────── + Continuous "alive" motion inside feature cards, inspired by + the design page platform-row visual animations. Combines a + gentle float with a sonar-pulse ring on the icon. */ +@keyframes icon-float { + 0%, 100% { transform: translateY(0); } + 50% { transform: translateY(-5px); } +} +@keyframes icon-ring { + 0% { box-shadow: 0 0 0 0 rgba(var(--color-primary-rgb), 0.35); } + 60% { box-shadow: 0 0 0 10px rgba(var(--color-primary-rgb), 0); } + 100% { box-shadow: 0 0 0 0 rgba(var(--color-primary-rgb), 0); } +} +@keyframes icon-cog-spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} +/* Float + ring on all feature icons (but only once the card has + scroll-revealed — the .is-animated class is set by demo-animator.js). */ +.oribi-card.is-animated .feature-icon { + animation: icon-float 3.6s ease-in-out infinite, + icon-ring 3.6s ease-out infinite; +} +/* Pause ambient animation during hover so the scale transform + from .oribi-card:hover .feature-icon takes over cleanly. */ +.oribi-card:hover .feature-icon { + animation: none; +} +/* Cog icons spin slowly — contextually appropriate regardless of card. */ +.feature-icon .fa-cog, +.feature-icon .fa-users-cog { + animation: icon-cog-spin 8s linear infinite; +} +/* Respect reduced-motion preference. */ +@media (prefers-reduced-motion: reduce) { + .oribi-card.is-animated .feature-icon { animation: none; } + .feature-icon .fa-cog, + .feature-icon .fa-users-cog { animation: none; } +} + /* Smooth theme transition */ body, .site-header, diff --git a/theme/assets/js/demo-animator.js b/theme/assets/js/demo-animator.js index fbafe12..23b52fa 100644 --- a/theme/assets/js/demo-animator.js +++ b/theme/assets/js/demo-animator.js @@ -17,15 +17,16 @@ if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; // 1. Assign incrementing --scroll-delay to every card within a feature-section. + // The block renders .grid-2/.grid-3/.grid-4 wrappers (no .feature-section class). // main.js's .scroll-visible rule reads this via var(--scroll-delay, 0s). - document.querySelectorAll('.feature-section').forEach(function (section) { - section.querySelectorAll('.oribi-card').forEach(function (card, i) { + document.querySelectorAll('.grid-2, .grid-3, .grid-4').forEach(function (grid) { + grid.querySelectorAll('.oribi-card').forEach(function (card, i) { card.style.setProperty('--scroll-delay', (i * STAGGER).toFixed(2) + 's'); }); }); // 2. Watch for scroll-visible being added to each card. - var cards = document.querySelectorAll('.feature-section .oribi-card'); + var cards = document.querySelectorAll('.grid-2 .oribi-card, .grid-3 .oribi-card, .grid-4 .oribi-card'); if (!cards.length) return; var mo = new MutationObserver(function (mutations) { @@ -44,6 +45,8 @@ icon.addEventListener('animationend', function () { icon.classList.remove('icon-pop'); icon.style.animationDelay = ''; + // Start ambient float+ring animation once the pop-in finishes. + card.classList.add('is-animated'); }, { once: true }); }