feat(footer): reveal circuit contacts on scroll

This commit is contained in:
2026-09-08 12:30:55 +03:30
parent 32dab06220
commit f9cf19c23f
5 changed files with 68 additions and 3 deletions

View File

@@ -348,6 +348,7 @@ export function IsoSignpost({
return (
<g
className={`iso-signpost ${isActive ? 'active' : ''}`}
data-footer-contact={_id}
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
onClick={onClick}

View File

@@ -118,6 +118,66 @@
overflow: visible;
}
/* Draw the circuit once on scroll, then reveal its four connected signposts. */
:global(html[data-motion-ready='true'])
.sceneContainer[data-footer-reveal]
:global(.footer-circuit-path) {
stroke-dasharray: 1;
stroke-dashoffset: 1;
transition: stroke-dashoffset var(--motion-duration-reveal) var(--motion-ease-in-out);
}
:global(html[data-motion-ready='true'])
.sceneContainer[data-footer-reveal][data-visible='true']
:global(.footer-circuit-path) {
stroke-dashoffset: 0;
}
:global(html[data-motion-ready='true'])
.sceneContainer[data-footer-reveal]
:global(.iso-signpost) {
opacity: 0;
transform: translateY(10px);
transform-box: fill-box;
transform-origin: center bottom;
transition:
opacity var(--motion-duration-default) var(--motion-ease-out),
transform var(--motion-duration-default) var(--motion-ease-out);
transition-delay: var(--motion-duration-reveal);
will-change: opacity, transform;
}
:global(html[data-motion-ready='true'])
.sceneContainer[data-footer-reveal]
:global(.iso-signpost[data-footer-contact='linkedin']) {
transition-delay: calc(var(--motion-duration-reveal) + var(--motion-stagger));
}
:global(html[data-motion-ready='true'])
.sceneContainer[data-footer-reveal]
:global(.iso-signpost[data-footer-contact='github']) {
transition-delay: calc(
var(--motion-duration-reveal) + var(--motion-stagger) + var(--motion-stagger)
);
}
:global(html[data-motion-ready='true'])
.sceneContainer[data-footer-reveal]
:global(.iso-signpost[data-footer-contact='telegram']) {
transition-delay: calc(
var(--motion-duration-reveal) + var(--motion-stagger) + var(--motion-stagger) +
var(--motion-stagger)
);
}
:global(html[data-motion-ready='true'])
.sceneContainer[data-footer-reveal][data-visible='true']
:global(.iso-signpost) {
opacity: 1;
transform: translateY(0);
will-change: auto;
}
/* --------------------------------------------------------------------------
Synchronized Contact Cards Grid
-------------------------------------------------------------------------- */

View File

@@ -85,7 +85,7 @@ export default function IsometricFooter() {
</header>
{/* 3D Isometric SVG Scene */}
<div className={styles.sceneContainer}>
<div className={styles.sceneContainer} data-footer-reveal>
<IsometricScene
activePost={activePost}
onPostHover={setActivePost}

View File

@@ -277,7 +277,9 @@ function CircuitConduits({ activePost }: { activePost: string | null }) {
{/* Left Branch: Phone -> Step 3 -> LinkedIn -> Center Platform with exact ±0.5 slope */}
<path
d="M 280,360 L 360,320 L 440,360 L 514,323"
d="M 514,323 L 440,360 L 360,320 L 280,360"
className="footer-circuit-path"
pathLength={1}
fill="none"
stroke="var(--accent-default, #3D8AFA)"
strokeWidth={2.5}
@@ -290,6 +292,8 @@ function CircuitConduits({ activePost }: { activePost: string | null }) {
{/* Right Branch: Center Platform -> GitHub -> Step 5 -> Telegram with exact ±0.5 slope */}
<path
d="M 686,323 L 760,360 L 840,320 L 920,360"
className="footer-circuit-path"
pathLength={1}
fill="none"
stroke="var(--accent-default, #3D8AFA)"
strokeWidth={2.5}

View File

@@ -2,7 +2,7 @@
import { useEffect } from 'react';
const REVEAL_SELECTOR = '[data-reveal]';
const REVEAL_SELECTOR = '[data-reveal], [data-footer-reveal]';
export const MotionController = () => {
useEffect(() => {