feat: Add ambient animations for feature icons and update scroll-delay logic for card entrance
This commit is contained in:
@@ -129,6 +129,46 @@ a:hover { color: var(--color-primary); }
|
|||||||
animation: icon-pop 0.4s ease-out backwards;
|
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 */
|
/* Smooth theme transition */
|
||||||
body,
|
body,
|
||||||
.site-header,
|
.site-header,
|
||||||
|
|||||||
@@ -17,15 +17,16 @@
|
|||||||
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
||||||
|
|
||||||
// 1. Assign incrementing --scroll-delay to every card within a feature-section.
|
// 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).
|
// main.js's .scroll-visible rule reads this via var(--scroll-delay, 0s).
|
||||||
document.querySelectorAll('.feature-section').forEach(function (section) {
|
document.querySelectorAll('.grid-2, .grid-3, .grid-4').forEach(function (grid) {
|
||||||
section.querySelectorAll('.oribi-card').forEach(function (card, i) {
|
grid.querySelectorAll('.oribi-card').forEach(function (card, i) {
|
||||||
card.style.setProperty('--scroll-delay', (i * STAGGER).toFixed(2) + 's');
|
card.style.setProperty('--scroll-delay', (i * STAGGER).toFixed(2) + 's');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. Watch for scroll-visible being added to each card.
|
// 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;
|
if (!cards.length) return;
|
||||||
|
|
||||||
var mo = new MutationObserver(function (mutations) {
|
var mo = new MutationObserver(function (mutations) {
|
||||||
@@ -44,6 +45,8 @@
|
|||||||
icon.addEventListener('animationend', function () {
|
icon.addEventListener('animationend', function () {
|
||||||
icon.classList.remove('icon-pop');
|
icon.classList.remove('icon-pop');
|
||||||
icon.style.animationDelay = '';
|
icon.style.animationDelay = '';
|
||||||
|
// Start ambient float+ring animation once the pop-in finishes.
|
||||||
|
card.classList.add('is-animated');
|
||||||
}, { once: true });
|
}, { once: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user