feat(footer): implement responsive 3D isometric SVG footer scene with theme tokens
Some checks failed
Test and deploy / Verify production build (push) Has been cancelled
Test and deploy / Deploy production (push) Has been cancelled

This commit is contained in:
2026-09-08 11:31:36 +03:30
parent 0ed36b3c32
commit 88adced09f
19 changed files with 1471 additions and 954 deletions

View File

@@ -0,0 +1,417 @@
import React from 'react';
import type { IsometricSceneProps } from './IsoTypes';
import {
MountainCluster,
PineTree,
IsoUprightPencil,
IsoMonitorDisplay,
IsoPCTower,
IsoSignpost,
PhoneIcon,
LinkedInIcon,
GitHubIcon,
TelegramIcon,
} from './IsoElements';
/**
* Faint Isometric Background Grid
* Fine architectural diamond grid lines covering the ground (slope 0.5, 2:1 isometric)
*/
function IsometricBackgroundGrid() {
const lines = [];
const spacing = 40;
const yStart = 150;
const yEnd = 500;
// Axis 1: / lines (slope 0.5, dx = 2 * dy)
for (let c = -600; c <= 1600; c += spacing * 2) {
const x1 = c;
const y1 = yStart;
const x2 = c + (yEnd - yStart) * 2;
const y2 = yEnd;
lines.push(
<line
key={`a1-${c}`}
x1={x1}
y1={y1}
x2={x2}
y2={y2}
stroke="var(--border-default, #D9E2EF)"
strokeWidth={1}
opacity={0.65}
/>
);
}
// Axis 2: \ lines (slope -0.5, dx = -2 * dy)
for (let c = -400; c <= 1800; c += spacing * 2) {
const x1 = c;
const y1 = yStart;
const x2 = c - (yEnd - yStart) * 2;
const y2 = yEnd;
lines.push(
<line
key={`a2-${c}`}
x1={x1}
y1={y1}
x2={x2}
y2={y2}
stroke="var(--border-default, #D9E2EF)"
strokeWidth={1}
opacity={0.65}
/>
);
}
return <g className="iso-ground-grid">{lines}</g>;
}
/**
* Architectural Stepped Terraces:
* 5 ground steps on the floor along the 2:1 isometric grid,
* plus the full-sized large center workstation platform with bold 20px blue drop apron
* and the elevated stepped workstation tier.
*/
function SteppedTerraces() {
return (
<g className="stepped-terraces">
<defs>
{/* Floor Step Fade Mask: solid at front (y=400..360), smooth fade backwards (y=210) */}
<linearGradient
id="floorFadeMaskGrad"
x1="0"
y1="410"
x2="0"
y2="210"
gradientUnits="userSpaceOnUse"
>
<stop offset="0%" stopColor="var(--bg-page, #F8FAFF)" stopOpacity="1" />
<stop offset="20%" stopColor="var(--bg-page, #F8FAFF)" stopOpacity="0.95" />
<stop offset="45%" stopColor="var(--bg-page, #F8FAFF)" stopOpacity="0.65" />
<stop offset="70%" stopColor="var(--bg-page, #F8FAFF)" stopOpacity="0.25" />
<stop offset="90%" stopColor="var(--bg-page, #F8FAFF)" stopOpacity="0.05" />
<stop offset="100%" stopColor="var(--bg-page, #F8FAFF)" stopOpacity="0" />
</linearGradient>
<mask
id="floorFadeMask"
maskUnits="userSpaceOnUse"
x="-100"
y="180"
width="1400"
height="260"
>
<rect x="-100" y="180" width="1400" height="260" fill="url(#floorFadeMaskGrad)" />
</mask>
</defs>
{/* 1. 3D FRONT VERTICAL DROP APRON (5 STEPS: Steps 2 to 6, x=200 to 1000) */}
<g id="terrace-front-drop-apron">
{/* Step 2 (Phone) */}
<polygon points="200,360 280,400 280,416 200,376" fill="var(--bg-subtle, #E6F0FE)" stroke="var(--border-default, #D9E2EF)" strokeWidth={1.2} />
<polygon points="360,360 280,400 280,416 360,376" fill="var(--color-border-light, #D9E2EF)" stroke="var(--border-default, #D9E2EF)" strokeWidth={1.2} />
{/* Step 3 (LinkedIn) */}
<polygon points="360,360 440,400 440,416 360,376" fill="var(--bg-subtle, #E6F0FE)" stroke="var(--border-default, #D9E2EF)" strokeWidth={1.2} />
<polygon points="520,360 440,400 440,416 520,376" fill="var(--color-border-light, #D9E2EF)" stroke="var(--border-default, #D9E2EF)" strokeWidth={1.2} />
{/* Step 4 (Center Platform) */}
<polygon points="520,360 600,400 600,416 520,376" fill="var(--bg-subtle, #E6F0FE)" stroke="var(--border-default, #D9E2EF)" strokeWidth={1.2} />
<polygon points="680,360 600,400 600,416 680,376" fill="var(--color-border-light, #D9E2EF)" stroke="var(--border-default, #D9E2EF)" strokeWidth={1.2} />
{/* Step 5 (GitHub) */}
<polygon points="680,360 760,400 760,416 680,376" fill="var(--bg-subtle, #E6F0FE)" stroke="var(--border-default, #D9E2EF)" strokeWidth={1.2} />
<polygon points="840,360 760,400 760,416 840,376" fill="var(--color-border-light, #D9E2EF)" stroke="var(--border-default, #D9E2EF)" strokeWidth={1.2} />
{/* Step 6 (Telegram) */}
<polygon points="840,360 920,400 920,416 840,376" fill="var(--bg-subtle, #E6F0FE)" stroke="var(--border-default, #D9E2EF)" strokeWidth={1.2} />
<polygon points="1000,360 920,400 920,416 1000,376" fill="var(--color-border-light, #D9E2EF)" stroke="var(--border-default, #D9E2EF)" strokeWidth={1.2} />
{/* Vertical corner edges at peaks */}
<line x1={280} y1={400} x2={280} y2={416} stroke="var(--border-strong, #A9BEDA)" strokeWidth={1.2} />
<line x1={440} y1={400} x2={440} y2={416} stroke="var(--border-strong, #A9BEDA)" strokeWidth={1.2} />
<line x1={600} y1={400} x2={600} y2={416} stroke="var(--border-strong, #A9BEDA)" strokeWidth={1.2} />
<line x1={760} y1={400} x2={760} y2={416} stroke="var(--border-strong, #A9BEDA)" strokeWidth={1.2} />
<line x1={920} y1={400} x2={920} y2={416} stroke="var(--border-strong, #A9BEDA)" strokeWidth={1.2} />
{/* Vertical corner edges at valleys and outer ends */}
<line x1={200} y1={360} x2={200} y2={376} stroke="var(--border-strong, #A9BEDA)" strokeWidth={1.2} />
<line x1={360} y1={360} x2={360} y2={376} stroke="var(--border-strong, #A9BEDA)" strokeWidth={1.2} />
<line x1={520} y1={360} x2={520} y2={376} stroke="var(--border-strong, #A9BEDA)" strokeWidth={1.2} />
<line x1={680} y1={360} x2={680} y2={376} stroke="var(--border-strong, #A9BEDA)" strokeWidth={1.2} />
<line x1={840} y1={360} x2={840} y2={376} stroke="var(--border-strong, #A9BEDA)" strokeWidth={1.2} />
<line x1={1000} y1={360} x2={1000} y2={376} stroke="var(--border-strong, #A9BEDA)" strokeWidth={1.2} />
</g>
{/* 2. FADING GROUND FLOOR TOP SURFACE & OUTLINE BORDERS */}
<g className="terrace-fading-top-surface" mask="url(#floorFadeMask)">
<polygon
points="200,360 280,400 360,360 440,400 520,360 600,400 680,360 760,400 840,360 920,400 1000,360 1000,200 200,200"
fill="var(--bg-page, #F8FAFF)"
/>
{/* Fading Diamond Tile Outlines */}
<g stroke="var(--border-default, #D9E2EF)" strokeWidth={1.05} opacity={0.65}>
<line x1={200} y1={360} x2={40} y2={280} />
<line x1={360} y1={360} x2={200} y2={280} />
<line x1={520} y1={360} x2={360} y2={280} />
<line x1={680} y1={360} x2={520} y2={280} />
<line x1={840} y1={360} x2={680} y2={280} />
<line x1={1000} y1={360} x2={840} y2={280} />
<line x1={280} y1={400} x2={440} y2={320} />
<line x1={440} y1={400} x2={600} y2={320} />
<line x1={600} y1={400} x2={760} y2={320} />
<line x1={760} y1={400} x2={920} y2={320} />
<line x1={920} y1={400} x2={1080} y2={320} />
</g>
</g>
{/* 3. CENTER ELEVATED WORKSTATION PLATFORM (TIER 1 & TIER 2) */}
<g id="workstation-elevated-platform">
{/* Tier 1 3D Base Diamond Platform with Sleek 15px Blue Drop Edge (248px wide) */}
{/* Left Front Drop Face (Slope +0.5 from 476,290 to 600,352, Drop Height = 15px) */}
<polygon
points="476,290 600,352 600,367 476,305"
fill="var(--hero-illus-cube-front, #2066AB)"
stroke="var(--hero-illus-cube-side, #0E4073)"
strokeWidth={0.9}
/>
{/* Right Front Drop Face (Slope -0.5 from 600,352 to 724,290, Drop Height = 15px) */}
<polygon
points="600,352 724,290 724,305 600,367"
fill="var(--hero-illus-cube-side, #0E4073)"
stroke="var(--hero-illus-cube-shadow, #0C234F)"
strokeWidth={0.9}
/>
{/* Center Vertical Ridge Line */}
<line x1={600} y1={352} x2={600} y2={367} stroke="var(--hero-illus-cube-shadow, #0C234F)" strokeWidth={1.2} />
{/* Tier 1 White Diamond Top Surface (Well-Proportioned 248px wide: x=476 to 724, y=228 to 352) */}
<polygon
points="476,290 600,352 724,290 600,228"
fill="var(--bg-raised, #FCFDFF)"
stroke="var(--border-strong, #A9BEDA)"
strokeWidth={1.1}
/>
{/* Tier 2: Refined & Compact 3D Elevated Stepped Workstation Platform (Drop Height = 12px) */}
{/* Front-left drop 1 (under monitor) */}
<polygon
points="506,285 538,301 538,313 506,297"
fill="var(--hero-illus-cube-front, #2066AB)"
stroke="var(--hero-illus-cube-side, #0E4073)"
strokeWidth={0.75}
/>
{/* Front-left drop 2 (middle step in front of pencil) */}
<polygon
points="538,301 570,317 570,329 538,313"
fill="var(--hero-illus-cube-front, #2066AB)"
stroke="var(--hero-illus-cube-side, #0E4073)"
strokeWidth={0.75}
/>
{/* Front-right drop (middle step facing SE) */}
<polygon
points="570,317 602,301 602,313 570,329"
fill="var(--hero-illus-cube-side, #0E4073)"
stroke="var(--hero-illus-cube-shadow, #0C234F)"
strokeWidth={0.75}
/>
{/* Inner corner step drop (facing right towards PC Tower) */}
<polygon
points="602,301 570,285 570,297 602,313"
fill="var(--hero-illus-cube-side, #0E4073)"
stroke="var(--hero-illus-cube-shadow, #0C234F)"
strokeWidth={0.75}
/>
<polygon
points="570,285 602,269 602,281 570,297"
fill="var(--hero-illus-cube-shadow, #0C234F)"
stroke="var(--color-black, #07132F)"
strokeWidth={0.75}
/>
{/* Tier 2: White Top Surfaces of the 3 Workstation Step Tiles */}
{/* Left Tile (holding Monitor) */}
<polygon
points="506,285 538,269 570,285 538,301"
fill="var(--bg-raised, #FCFDFF)"
stroke="var(--border-strong, #A9BEDA)"
strokeWidth={0.9}
/>
{/* Center Middle Tile */}
<polygon
points="538,301 570,285 602,301 570,317"
fill="var(--bg-raised, #FCFDFF)"
stroke="var(--border-strong, #A9BEDA)"
strokeWidth={0.9}
/>
{/* Back Tile (holding Pencil) */}
<polygon
points="538,269 570,253 602,269 570,285"
fill="var(--bg-raised, #FCFDFF)"
stroke="var(--border-strong, #A9BEDA)"
strokeWidth={0.9}
/>
{/* Dividing Grid Lines on the Step */}
<line x1={538} y1={301} x2={570} y2={285} stroke="var(--border-default, #D9E2EF)" strokeWidth={0.8} />
<line x1={570} y1={285} x2={602} y2={301} stroke="var(--border-default, #D9E2EF)" strokeWidth={0.8} />
<line x1={538} y1={269} x2={570} y2={285} stroke="var(--border-default, #D9E2EF)" strokeWidth={0.8} />
</g>
</g>
);
}
/**
* Glowing Blue Circuit Lines
* Completely uniform 2:1 isometric slope (26.565°, dy/dx = ±0.5) across ALL conduit segments
*/
function CircuitConduits({ activePost }: { activePost: string | null }) {
return (
<g className="circuit-conduits">
<defs>
<filter id="circuitGlow" x="-20%" y="-20%" width="140%" height="140%">
<feDropShadow dx="0" dy="0" stdDeviation="2" floodColor="var(--accent-default, #3D8AFA)" floodOpacity="0.6" />
</filter>
</defs>
{/* 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"
fill="none"
stroke="var(--accent-default, #3D8AFA)"
strokeWidth={2.5}
strokeLinecap="round"
strokeLinejoin="miter"
filter="url(#circuitGlow)"
opacity={activePost === 'phone' || activePost === 'linkedin' ? 1 : 0.85}
/>
{/* 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"
fill="none"
stroke="var(--accent-default, #3D8AFA)"
strokeWidth={2.5}
strokeLinecap="round"
strokeLinejoin="miter"
filter="url(#circuitGlow)"
opacity={activePost === 'github' || activePost === 'telegram' ? 1 : 0.85}
/>
</g>
);
}
/**
* Main Isometric Footer Scene SVG Component
*/
export default function IsometricScene({
activePost,
onPostHover,
onPostClick,
}: IsometricSceneProps) {
return (
<svg
viewBox="0 175 1200 290"
xmlns="http://www.w3.org/2000/svg"
className="isometric-svg"
preserveAspectRatio="xMidYMax meet"
role="img"
aria-label="Interactive 3D isometric footer scene"
>
{/* 1. Faint Isometric Ground Grid */}
<IsometricBackgroundGrid />
{/* 2. Solid 3D Architectural Stepped Terraces */}
<SteppedTerraces />
{/* 3. Glowing Royal Blue Circuit Conduits */}
<CircuitConduits activePost={activePost} />
{/* 4. Left Mountain Cluster */}
<MountainCluster x={80} y={250} scale={0.92} />
{/* 5. Right Mountain Cluster (mirrored) */}
<MountainCluster x={1120} y={250} flip={true} scale={0.92} />
{/* 6. Pine Trees on the floor along ground line */}
<PineTree x={335} y={425} scale={0.84} dark={true} />
<PineTree x={395} y={422} scale={0.96} dark={false} />
<PineTree x={490} y={422} scale={0.92} />
<PineTree x={810} y={422} scale={0.96} dark={false} />
<PineTree x={865} y={425} scale={0.84} dark={true} />
<PineTree x={965} y={422} scale={0.92} />
{/* 7. Central Workstation Illustrations resting flush on the large platform */}
<g className="central-workstation">
{/* Prominent Monitor Display resting on left elevated step */}
<IsoMonitorDisplay x={534} y={285} />
{/* Majestic Upright Faceted Pencil resting on back elevated step */}
<IsoUprightPencil x={570} y={269} height={80} width={17} />
{/* Vertical PC Tower on Pedestal Plate resting on Tier 1 */}
<IsoPCTower x={642} y={290} size={20} height={40} />
</g>
{/* 8. The 4 Interactive Isometric Signposts rotated in 3D */}
<IsoSignpost
id="phone"
x={280}
y={360}
postHeight={48}
facing="right"
backdropDirection="right-top"
icon={<PhoneIcon />}
label="09388263077"
isActive={activePost === 'phone'}
onMouseEnter={() => onPostHover?.('phone')}
onMouseLeave={() => onPostHover?.(null)}
onClick={() => onPostClick?.('phone')}
/>
<IsoSignpost
id="linkedin"
x={440}
y={360}
postHeight={48}
facing="right"
backdropDirection="right-top"
icon={<LinkedInIcon />}
label="@amiirkhl"
isActive={activePost === 'linkedin'}
onMouseEnter={() => onPostHover?.('linkedin')}
onMouseLeave={() => onPostHover?.(null)}
onClick={() => onPostClick?.('linkedin')}
/>
<IsoSignpost
id="github"
x={760}
y={360}
postHeight={48}
facing="left"
backdropDirection="left-top"
icon={<GitHubIcon />}
label="git.amiirkhl.ir"
isActive={activePost === 'github'}
onMouseEnter={() => onPostHover?.('github')}
onMouseLeave={() => onPostHover?.(null)}
onClick={() => onPostClick?.('github')}
/>
<IsoSignpost
id="telegram"
x={920}
y={360}
postHeight={48}
facing="left"
backdropDirection="left-top"
icon={<TelegramIcon />}
label="@amiirkhl"
isActive={activePost === 'telegram'}
onMouseEnter={() => onPostHover?.('telegram')}
onMouseLeave={() => onPostHover?.(null)}
onClick={() => onPostClick?.('telegram')}
/>
</svg>
);
}