Polish: subtle card hover-lift + scroll-fade-in for sections (respects reduced-motion)
This commit is contained in:
parent
aa3e1bd0cc
commit
141349c95e
62
index.html
62
index.html
@ -30,6 +30,47 @@
|
|||||||
|
|
||||||
html { scroll-behavior: smooth; }
|
html { scroll-behavior: smooth; }
|
||||||
|
|
||||||
|
/* ── POLISH: shared hover transition for all card variants ── */
|
||||||
|
.tool-card, .create-card, .plan-step, .gloss-item, .project-card, .prompt-card, .eq-box {
|
||||||
|
transition: transform 180ms ease, box-shadow 180ms ease, border-color 180ms ease;
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
@media (hover: hover) {
|
||||||
|
.tool-card:hover, .create-card:hover, .plan-step:hover,
|
||||||
|
.gloss-item:hover, .prompt-card:hover, .eq-box:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 1px 2px rgba(0,0,0,0.04), 0 8px 24px rgba(0,0,0,0.06);
|
||||||
|
}
|
||||||
|
/* project-card already has its own hover (border + ring) — strengthen with lift */
|
||||||
|
.project-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 1px 2px rgba(0,0,0,0.04), 0 8px 24px rgba(0,0,0,0.06);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.tool-card, .create-card, .plan-step, .gloss-item, .project-card, .prompt-card, .eq-box {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
.tool-card:hover, .create-card:hover, .plan-step:hover,
|
||||||
|
.gloss-item:hover, .project-card:hover, .prompt-card:hover, .eq-box:hover {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── POLISH: scroll-triggered fade-in ── */
|
||||||
|
.reveal {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(8px);
|
||||||
|
transition: opacity 350ms ease, transform 350ms ease;
|
||||||
|
}
|
||||||
|
.reveal.in {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.reveal { opacity: 1; transform: none; transition: none; }
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
@ -2413,6 +2454,27 @@ const SCHEDULE = [
|
|||||||
});
|
});
|
||||||
|
|
||||||
refreshHeader();
|
refreshHeader();
|
||||||
|
|
||||||
|
/* ── POLISH: scroll-triggered fade-in ────────────────────
|
||||||
|
Marks all direct content blocks inside <section class="section">
|
||||||
|
with .reveal, then toggles .in on intersection.
|
||||||
|
Respects prefers-reduced-motion via CSS fallback. */
|
||||||
|
(function initReveal() {
|
||||||
|
if (!('IntersectionObserver' in window)) return;
|
||||||
|
const targets = document.querySelectorAll(
|
||||||
|
'.section .container > *, .section .container .section-header > *'
|
||||||
|
);
|
||||||
|
targets.forEach(el => el.classList.add('reveal'));
|
||||||
|
const io = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(e => {
|
||||||
|
if (e.isIntersecting) {
|
||||||
|
e.target.classList.add('in');
|
||||||
|
io.unobserve(e.target);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, { rootMargin: '0px 0px -60px 0px', threshold: 0.05 });
|
||||||
|
targets.forEach(el => io.observe(el));
|
||||||
|
})();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user