Task 9: Implement scroll animations and responsive design

- Add IntersectionObserver-based scroll reveal for all sections
- Sections start at opacity:0/translateY(24px), animate to visible on scroll
- Hero section immediately visible (above fold, no animation)
- Staggered child card/item animations with 60ms delay per item
- Animations fire once only (observer unobserved after reveal)
- Fix hover transform specificity for cards, education, projects
- Fix vital-card hover at hero-level specificity

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-09 13:26:44 +00:00
parent 7365d2cbac
commit 4d27cb5dc1
+107 -1
View File
@@ -755,6 +755,72 @@
margin-top: 2px; margin-top: 2px;
} }
/* =========================================
SCROLL REVEAL ANIMATIONS
========================================= */
.cv-main section {
opacity: 0;
transform: translateY(24px);
transition: opacity 0.6s ease, transform 0.6s ease;
}
.cv-main section.visible {
opacity: 1;
transform: translateY(0);
}
/* Hero section should be immediately visible (above fold) */
.cv-main section.hero {
opacity: 1;
transform: none;
transition: none;
}
/* Staggered child card/item animations */
.cv-main section .vital-card,
.cv-main section .skill-item,
.cv-main section .timeline-entry,
.cv-main section .education-card,
.cv-main section .project-card,
.cv-main section .contact-item {
opacity: 0;
transform: translateY(16px);
transition: opacity 0.5s ease, transform 0.5s ease;
}
.cv-main section.visible .vital-card,
.cv-main section.visible .skill-item,
.cv-main section.visible .timeline-entry,
.cv-main section.visible .education-card,
.cv-main section.visible .project-card,
.cv-main section.visible .contact-item {
opacity: 1;
transform: translateY(0);
}
/* Re-enable hover transforms at matching specificity */
.cv-main section.visible .education-card:hover,
.cv-main section.visible .project-card:hover {
transform: translateY(-2px);
}
.cv-main section.visible .timeline-entry .timeline-card:hover {
transform: scale(1.01);
}
/* Hero vital cards get their own reveal since hero is always visible */
.cv-main .hero .vital-card {
opacity: 1;
transform: none;
transition: box-shadow 0.3s ease, transform 0.3s ease;
}
.cv-main .hero .vital-card:hover {
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
/* ========================================= /* =========================================
FOOTER FOOTER
========================================= */ ========================================= */
@@ -1686,9 +1752,10 @@
var cvContent = document.getElementById('cv-content'); var cvContent = document.getElementById('cv-content');
cvContent.classList.add('revealed'); cvContent.classList.add('revealed');
// Initialize nav tracking, smooth scroll, and skill gauges after reveal // Initialize nav tracking, smooth scroll, skill gauges, and scroll reveal after reveal
initNavTracking(); initNavTracking();
initSkillGauges(); initSkillGauges();
initScrollReveal();
}, 500); }, 500);
} }
@@ -1777,6 +1844,45 @@
gaugeObserver.observe(skillsSection); gaugeObserver.observe(skillsSection);
} }
/* =========================================
SCROLL REVEAL: Sections & staggered children
========================================= */
function initScrollReveal() {
var sections = document.querySelectorAll('.cv-main section');
var revealObserver = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
var section = entry.target;
section.classList.add('visible');
// Stagger child cards/items
var children = section.querySelectorAll(
'.vital-card, .skill-item, .timeline-entry, .education-card, .project-card, .contact-item'
);
children.forEach(function(child, i) {
child.style.transitionDelay = (i * 60) + 'ms';
});
// Only animate once — stop observing after reveal
revealObserver.unobserve(section);
}
});
}, {
threshold: 0.15
});
sections.forEach(function(section) {
// Hero is always visible (above the fold), skip it
if (section.classList.contains('hero')) {
section.classList.add('visible');
return;
}
revealObserver.observe(section);
});
}
})(); })();
</script> </script>
</body> </body>