feat: Implement ScrollReveal animations and sync navigation icons

- Add ScrollReveal component with Intersection Observer API
- Create useIntersectionObserver hook for scroll-based animations
- Implement progressive scroll animations on motion page (200ms-600ms delays)
- Add CSS animation system with prefers-reduced-motion accessibility support
- Update navigation icons for consistency between sidebar and primary nav
- Use Interface/Trending_Up for Motion System and Environment/Puzzle for Components
- Add GPU-accelerated transforms with will-change optimization

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
tony
2025-08-26 08:39:48 +10:00
parent 9cf92f9fcf
commit 630d1c26ad
12 changed files with 1009 additions and 810 deletions

View File

@@ -1,4 +1,4 @@
import ScrollReveal from '@/components/ScrollReveal';
export default function ColorsPage() {
const lightColorSets = [
{
@@ -170,7 +170,9 @@ export default function ColorsPage() {
<div className="py-chorus-xxl bg-gradient-to-b dark:from-carbon-950 dark:to-mulberry-950 from-white to-sand-200">
<section id="colors" className="pt-chorus-xxl">
<div className="px-chorus-lg max-w-5xl mx-auto">
<ScrollReveal delay={300} duration={600} direction="up">
<h2 className="text-h2 mb-chorus-lg">Color Palette</h2>
</ScrollReveal>
<div className="space-y-chorus-lg">
<div>
@@ -183,6 +185,7 @@ export default function ColorsPage() {
<div className="grid md:grid-cols-2 mb-chorus-lg">
<div className="p-chorus-lg bg-mulberry-900 shadow-lg">
<ScrollReveal delay={300} duration={600} direction="up">
<div className="flex items-center gap-3 mb-chorus-sm">
<img src="/icons/coolicons.v4.1/coolicons PNG/White/Environment/Moon.png" alt="Dark Mode" className="w-8 h-8" />
<h4 className="text-h4 text-white">Dark Mode (Default)</h4>
@@ -194,9 +197,11 @@ export default function ColorsPage() {
<li><strong>Aesthetic:</strong> Ultra-minimalist with sophisticated mulberry accents</li>
<li><strong>Contrast:</strong> WCAG 2.1 AA compliant</li>
</ul>
</ScrollReveal>
<div className="-mr-chorus-lg">
{darkColorSets.map((colorSet) => (
<div key={colorSet.name} className={`border border-nickel-200 overflow-hidden bg-${colorSet.baseColor}`}>
<ScrollReveal delay={300} duration={600} direction="up">
<div className={`p-chorus-lg bg-${colorSet.baseColor}`}>
<div className="flex items-start justify-between mb-chorus-md">
<div>
@@ -226,6 +231,7 @@ export default function ColorsPage() {
})}
</div>
</div>
</ScrollReveal>
</div>
))}
</div>
@@ -233,6 +239,8 @@ export default function ColorsPage() {
</div>
<div className="p-chorus-lg bg-white shadow-lg">
<ScrollReveal delay={300} duration={600} direction="up">
<div className="flex items-center gap-3 mb-chorus-sm">
<img src="/icons/coolicons.v4.1/coolicons PNG/Black/Environment/Sun.png" alt="Light Mode" className="w-8 h-8" />
<h4 className="text-h4 text-carbon-900">Light Mode (Alternative)</h4>
@@ -244,10 +252,13 @@ export default function ColorsPage() {
<li><strong>Usage:</strong> Print materials, accessibility accommodations</li>
<li><strong>Contrast:</strong> Optimized for readability on warm backgrounds</li>
</ul>
</ScrollReveal>
<div className="-ml-chorus-lg">
{lightColorSets.map((colorSet) => (
<div key={colorSet.name} className={`border border-nickel-200 overflow-hidden bg-${colorSet.baseColor}`}>
<ScrollReveal delay={300} duration={600} direction="up">
<div className={`p-chorus-lg bg-${colorSet.baseColor}`}>
<div className="flex items-start justify-between mb-chorus-md">
<div>
@@ -277,6 +288,7 @@ export default function ColorsPage() {
})}
</div>
</div>
</ScrollReveal>
</div>
))}
</div>

View File

@@ -51,30 +51,30 @@ export default function Communications() {
<div className="border border-nickel-200 p-chorus-lg dark:border-nickel-900 ">
<h3 className="text-h3 mb-chorus-md">Writing Style</h3>
<ul className="space-y-2 text-sm text-carbon-600 dark:text-mulberry-300">
<li> Active voice preferred</li>
<li> Clear, concise sentences</li>
<li> Technical accuracy</li>
<li> Inclusive language</li>
<li>Active voice preferred</li>
<li>Clear, concise sentences</li>
<li>Technical accuracy</li>
<li>Inclusive language</li>
</ul>
</div>
<div className="border border-nickel-200 p-chorus-lg dark:border-nickel-900 ">
<h3 className="text-h3 mb-chorus-md">Terminology</h3>
<ul className="space-y-2 text-sm text-carbon-600 dark:text-mulberry-300">
<li> "AI agents" not "bots"</li>
<li> "Context management"</li>
<li> "Distributed orchestration"</li>
<li> "Knowledge fabric"</li>
<li>"AI agents" not "bots"</li>
<li>"Context management"</li>
<li>"Distributed orchestration"</li>
<li>"Knowledge fabric"</li>
</ul>
</div>
<div className="border border-nickel-200 p-chorus-lg dark:border-nickel-900 ">
<h3 className="text-h3 mb-chorus-md">Localization</h3>
<ul className="space-y-2 text-sm text-carbon-600 dark:text-mulberry-300">
<li> Global-first approach</li>
<li> Cultural sensitivity</li>
<li> Technical term consistency</li>
<li> Regional adaptations</li>
<li>Global-first approach</li>
<li>Cultural sensitivity</li>
<li>Technical term consistency</li>
<li>Regional adaptations</li>
</ul>
</div>
</div>

View File

@@ -790,6 +790,10 @@ pre[class*="language-"] {
margin-bottom: 0;
}
.secondary-nav {
overflow: hidden !important;
}
.primary-nav {
position: relative;
font-weight: 600;
@@ -1104,6 +1108,22 @@ nav button {
&.dark .bg-white { background-color: #ffffff !important; }
}
/* Scroll Animation System */
.scroll-reveal {
/* Base styles for scroll animations */
will-change: opacity, transform;
}
/* Accessibility: respect prefers-reduced-motion */
@media (prefers-reduced-motion: reduce) {
.scroll-reveal, .scroll-reveal * {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
transform: none !important;
opacity: 1 !important;
}
}
/* Enhanced contrast for accessibility theme selection indicators */
[data-theme="protanopia"] .ring-ocean-500 { box-shadow: 0 0 0 2px #1e40af !important; }
[data-theme="deuteranopia"] .ring-eucalyptus-500 { box-shadow: 0 0 0 2px #6b21a8 !important; }

View File

@@ -1,10 +1,12 @@
import { div } from "three/tsl"
import ScrollReveal from '@/components/ScrollReveal';
export default function IconographyPage() {
return (
<div className="py-chorus-xxl">
<section>
<div className="max-w-5xl mx-auto px-chorus-lg pt-chorus-xxl">
<ScrollReveal delay={200} duration={600} direction="up">
<div className="mb-chorus-xxl">
<h1 className="text-h2 mb-chorus-lg">Iconography System</h1>
@@ -15,23 +17,31 @@ export default function IconographyPage() {
</p>
<div className="grid gap-chorus-md md:grid-cols-3 mt-chorus-lg">
<ScrollReveal delay={300} duration={600} direction="up">
<div className="border border-nickel-200 p-chorus-md dark:border-nickel-900 bg-white dark:bg-mulberry-950">
<h4 className="text-h4 mb-chorus-sm">Clarity & Recognition</h4>
<p className="text-sm text-carbon-600 dark:text-mulberry-300">Icons communicate instantly without language barriers</p>
</div>
</ScrollReveal>
<ScrollReveal delay={400} duration={600} direction="up">
<div className="border border-nickel-200 p-chorus-md dark:border-nickel-900 bg-white dark:bg-mulberry-950">
<h4 className="text-h4 mb-chorus-sm">Theme Adaptive</h4>
<p className="text-sm text-carbon-600 dark:text-mulberry-300">Black and white variants ensure perfect contrast in all modes</p>
</div>
</ScrollReveal>
<ScrollReveal delay={500} duration={600} direction="up">
<div className="border border-nickel-200 p-chorus-md dark:border-nickel-900 bg-white dark:bg-mulberry-950">
<h4 className="text-h4 mb-chorus-sm">Consistent Scale</h4>
<p className="text-sm text-carbon-600 dark:text-mulberry-300">Standardized sizing maintains visual harmony</p>
</div>
</div>
</ScrollReveal>
</div>
</div>
</div>
</ScrollReveal>
{/* Icon Categories */}
<section className="mb-chorus-xxl">
<h2 className="text-h3 mb-chorus-md">Icon Categories</h2>
<p className="mb-chorus-md">
@@ -39,6 +49,7 @@ export default function IconographyPage() {
</p>
<div className="space-y-chorus-lg">
<ScrollReveal delay={500} duration={600} direction="up">
{/* Interface Icons */}
<div className="shadow-lg bg-white dark:bg-mulberry-900 border border-nickel-200 p-chorus-lg dark:border-nickel-800">
<h4 className="text-h4 mb-chorus-md">Interface & Navigation</h4>
@@ -86,7 +97,9 @@ export default function IconographyPage() {
</div>
</div>
</div>
</ScrollReveal>
<ScrollReveal delay={500} duration={600} direction="up">
{/* Navigation Icons */}
<div className="shadow-lg bg-white dark:bg-mulberry-900 border border-nickel-200 p-chorus-lg dark:border-nickel-800">
<h4 className="text-h4 mb-chorus-md">Navigation & Direction</h4>
@@ -134,7 +147,8 @@ export default function IconographyPage() {
</div>
</div>
</div>
</ScrollReveal>
<ScrollReveal delay={500} duration={600} direction="up">
{/* File & System Icons */}
<div className="shadow-lg bg-white dark:bg-mulberry-900 border border-nickel-200 p-chorus-lg dark:border-nickel-800">
<h4 className="text-h4 mb-chorus-md">File & System Operations</h4>
@@ -182,7 +196,8 @@ export default function IconographyPage() {
</div>
</div>
</div>
</ScrollReveal>
<ScrollReveal delay={500} duration={600} direction="up">
{/* Communication Icons */}
<div className="shadow-lg bg-white dark:bg-mulberry-900 border border-nickel-200 p-chorus-lg dark:border-nickel-800">
<h4 className="text-h4 mb-chorus-md">Communication & Social</h4>
@@ -230,7 +245,8 @@ export default function IconographyPage() {
</div>
</div>
</div>
</ScrollReveal>
<ScrollReveal delay={500} duration={600} direction="up">
{/* Status & Warning Icons */}
<div className="shadow-lg bg-white dark:bg-mulberry-900 border border-nickel-200 p-chorus-lg dark:border-nickel-800">
<h4 className="text-h4 mb-chorus-md">Status & Feedback</h4>
@@ -278,9 +294,11 @@ export default function IconographyPage() {
</div>
</div>
</div>
</ScrollReveal>
</div>
</section>
<ScrollReveal delay={200} duration={600} direction="up">
{/* Usage Guidelines */}
<section className="mb-chorus-xxl">
<h2 className="text-h3 mb-chorus-md">Usage Guidelines</h2>
@@ -360,7 +378,9 @@ export default function IconographyPage() {
</div>
</div>
</section>
</ScrollReveal>
<ScrollReveal delay={200} duration={600} direction="up">
{/* Implementation Guide */}
<section>
<h2 className="text-h3 mb-chorus-md">Implementation Guide</h2>
@@ -423,6 +443,7 @@ export default function IconographyPage() {
</div>
</div>
</section>
</ScrollReveal>
</div>
</section>
</div>

View File

@@ -1,26 +1,36 @@
import ThreeLogo from '@/components/ThreeLogo';
import ScrollReveal from '@/components/ScrollReveal';
export default function LogoPage() {
return (
<div className="py-chorus-xxl bg-gradient-to-b dark:from-mulberry-700 dark:to-mulberry-950 to-white from-sand-400 ">
{/* Full-width Hero Section with 3D Logo */}
<section id="logo-hero" className="mb-chorus-lg pt-chorus-xl">
<div className="relative">
{/* Fixed Hero Section with 3D Logo */}
<section
id="logo-hero"
className="fixed top-0 left-0 right-0 bottom-0 md:left-64 flex items-center justify-center bg-gradient-to-b dark:from-mulberry-700 dark:to-mulberry-950 to-white from-sand-400"
>
<ScrollReveal delay={300} duration={600} direction="up">
{/* Logo Section */}
<div className="pt-chorus-xs logo-group-circular mx-auto">
<div className="pb-chorus-md item mx-auto logo-group-circular">
<div className="logo-group-circular">
<div className="item logo-group-circular">
<ThreeLogo width={512} height={512} />
<h4 className="text-h4 font-logo">CHORUS</h4>
</div>
</div>
</ScrollReveal>
</section>
{/* Spacer to create scroll space */}
<div className="h-screen"></div>
{/* Sliding Content Overlay */}
<div className="relative z-10 bg-white dark:bg-transparent shadow-2xl">
<div className="max-w-5xl mx-auto px-chorus-lg py-chorus-xxl">
{/* Logo System Content */}
<div className="space-y-chorus-xxl">
{/* Primary Logo Description */}
<ScrollReveal delay={300} duration={600} direction="up">
<section>
<h2 className="text-h2 mb-chorus-lg">Logo System</h2>
@@ -65,7 +75,7 @@ export default function LogoPage() {
</div>
</div>
</section>
</ScrollReveal>
{/* Logo Orientations */}
<section>
<h3 className="text-h3 mb-chorus-md">Logo Orientations</h3>
@@ -263,5 +273,6 @@ export default function LogoPage() {
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,3 +1,5 @@
import ScrollReveal from '@/components/ScrollReveal';
export default function Motion() {
return (
<div className="py-chorus-xxl bg-gradient-to-b dark:from-mulberry-950 dark:to-carbon-950 from-white to-sand-200">
@@ -6,6 +8,7 @@ export default function Motion() {
<h2 className="text-h2 mb-chorus-lg">Motion System</h2>
<div className="py-chorus-lg space-y-chorus-lg">
<ScrollReveal delay={200} duration={600} direction="up">
<div className="mb-chorus-lg">
<p className="text-lg mb-chorus-md">
The CHORUS motion system creates sophisticated, purposeful animations that enhance usability without overwhelming users.
@@ -14,7 +17,9 @@ export default function Motion() {
Our implementation focuses on performance, accessibility, and consistent timing that reinforces the premium CHORUS brand personality.
</p>
</div>
</ScrollReveal>
<ScrollReveal delay={300} duration={600} direction="up">
<div className="grid gap-chorus-lg md:grid-cols-3">
<div className="border border-nickel-200 p-chorus-lg dark:border-nickel-900">
<h3 className="text-h3 mb-chorus-md">Timing Scale</h3>
@@ -62,7 +67,9 @@ export default function Motion() {
</div>
</div>
</div>
</ScrollReveal>
<ScrollReveal delay={400} duration={600} direction="up">
<div className="grid gap-chorus-lg md:grid-cols-2">
<div className="bg-sand-50 dark:bg-mulberry-900 p-chorus-lg border border-sand-200 dark:border-mulberry-800">
<h3 className="text-h3 mb-chorus-md">Interactive Elements</h3>
@@ -113,7 +120,9 @@ export default function Motion() {
</div>
</div>
</div>
</ScrollReveal>
<ScrollReveal delay={500} duration={600} direction="up">
<div className="bg-white dark:bg-carbon-900 p-chorus-lg border border-nickel-200 dark:border-carbon-800">
<h3 className="text-h3 mb-chorus-md">CSS Implementation</h3>
<div className="grid gap-chorus-md md:grid-cols-2">
@@ -145,7 +154,9 @@ export default function Motion() {
</div>
</div>
</div>
</ScrollReveal>
<ScrollReveal delay={600} duration={600} direction="up">
<div className="bg-sand-50 dark:bg-mulberry-900 p-chorus-lg border border-sand-200 dark:border-mulberry-800">
<h3 className="text-h3 mb-chorus-md">Motion Principles</h3>
<div className="grid gap-chorus-md md:grid-cols-2">
@@ -171,6 +182,7 @@ export default function Motion() {
</div>
</div>
</div>
</ScrollReveal>
</div>
</div>
</section>

View File

@@ -1,9 +1,11 @@
import ScrollReveal from '@/components/ScrollReveal';
export default function TypographyPage() {
return (
<div className="py-chorus-xxl">
<section className="mb-chorus-xxl pt-chorus-xxl">
<div className="mb-chorus-xxl max-w-5xl mx-auto px-chorus-lg ">
<ScrollReveal delay={300} duration={600} direction="up">
<h1 className="text-h2 mb-chorus-md">Typography</h1>
<p className="text-carbon-600 dark:text-mulberry-300 mb-chorus-md">
@@ -16,7 +18,9 @@ export default function TypographyPage() {
All spacing, sizing, and proportions automatically scale with the 18px base. CHORUS spacing system (chorus-xs, chorus-md, etc.) maintains perfect proportional relationships.
</p>
</div>
</ScrollReveal>
<ScrollReveal delay={400} duration={600} direction="up">
{/* Font Families */}
<div className="py-chorus-xl">
<h2 className="text-h3 mb-chorus-md">Font Families</h2>
@@ -64,8 +68,9 @@ export default function TypographyPage() {
</div>
</div>
</div>
</ScrollReveal>
<ScrollReveal delay={500} duration={600} direction="up">
{/* Typography Examples - DO vs DON'T */}
<div className="py-chorus-xl">
<div className="grid gap-chorus-lg md:grid-cols-2">
@@ -189,14 +194,19 @@ export default function TypographyPage() {
</div>
</div>
</div>
</ScrollReveal>
<ScrollReveal delay={600} duration={600} direction="up">
{/* Typography Scale */}
<h2 className="text-h3 mb-chorus-md">Proportional Font Scale</h2>
<p className="text-sm text-carbon-500 dark:text-mulberry-300 mb-chorus-lg">
Based on Major Third (1.25×) ratio with 18px foundation for enhanced readability
</p>
</ScrollReveal>
<ScrollReveal delay={700} duration={600} direction="up">
<div className="bg-white dark:bg-mulberry-950 border border-nickel-200 dark:border-mulberry-800 p-chorus-lg">
<div className="grid gap-chorus-sm md:grid-cols-2">
<div>
@@ -219,8 +229,10 @@ export default function TypographyPage() {
</div>
</div>
</div>
</ScrollReveal>
<ScrollReveal delay={800} duration={600} direction="up">
<div className="pt-chorus-xl">
<h2 className="text-h3 mb-chorus-md">Implementation Guide</h2>
<p className="text-carbon-600 dark:text-mulberry-300 mb-chorus-lg">
@@ -261,7 +273,9 @@ export default function TypographyPage() {
</div>
</div>
</div>
</div>
</ScrollReveal>
</div>
</section>
</div>

View File

@@ -25,10 +25,8 @@ export default function Footer() {
<h4 className="text-h4 text-carbon-950 dark:text-white mb-chorus-md">Brand Guide</h4>
<ul className="space-y-chorus-xs text-sm">
<li><Link href="/" className="text-carbon-600 dark:text-mulberry-300 hover:text-carbon-950 dark:hover:text-white transition-colors">Overview</Link></li>
<li><Link href="/identity" className="text-carbon-600 dark:text-mulberry-300 hover:text-carbon-950 dark:hover:text-white transition-colors">Brand Identity</Link></li>
<li><Link href="/colors" className="text-carbon-600 dark:text-mulberry-300 hover:text-carbon-950 dark:hover:text-white transition-colors">Color Palette</Link></li>
<li><Link href="/typography" className="text-carbon-600 dark:text-mulberry-300 hover:text-carbon-950 dark:hover:text-white transition-colors">Typography</Link></li>
<li><Link href="/logo" className="text-carbon-600 dark:text-mulberry-300 hover:text-carbon-950 dark:hover:text-white transition-colors">Logo System</Link></li>
<li><Link href="/visual-identity" className="text-carbon-600 dark:text-mulberry-300 hover:text-carbon-950 dark:hover:text-white transition-colors">Visual Identity</Link></li>
<li><Link href="/communications" className="text-carbon-600 dark:text-mulberry-300 hover:text-carbon-950 dark:hover:text-white transition-colors">Communications</Link></li>
</ul>
</div>
<div>

View File

@@ -39,8 +39,8 @@ const MobileAccordionMenu = ({ onClose }: MobileAccordionMenuProps) => {
{ href: '/colors', section: 'colors', icon: 'Edit/Swatches_Palette', label: 'Color Palette' },
{ href: '/iconography', section: 'iconography', icon: 'Interface/Main_Component', label: 'Iconography' },
{ href: '/accessibility', section: 'accessibility', icon: 'Interface/Heart_01', label: 'Accessibility' },
{ href: '/components', section: 'components', icon: 'Interface/Settings', label: 'Components' },
{ href: '/motion', section: 'motion', icon: 'Interface/Link', label: 'Motion System' },
{ href: '/components', section: 'components', icon: 'Environment/Puzzle', label: 'Components' },
{ href: '/motion', section: 'motion', icon: 'Interface/Trending_Up', label: 'Motion System' },
{ href: '/implementation', section: 'implementation', icon: 'File/File_Code', label: 'Implementation' },
]
},
@@ -95,7 +95,7 @@ const MobileAccordionMenu = ({ onClose }: MobileAccordionMenuProps) => {
className={`w-full group flex items-center justify-between gap-2 px-chorus-sm py-chorus-xs text-left transition-all duration-300 ease-out hover:bg-sand-50 hover:bg-opacity-30 hover:text-mulberry-950 dark:hover:bg-mulberry-900 dark:hover:bg-opacity-40 dark:hover:text-white ${
isActive
? 'bg-sand-100 text-mulberry-950 dark:bg-mulberry-800 dark:text-white font-medium'
: 'text-carbon-700 dark:text-mulberry-300'
: 'transparent'
}`}
aria-expanded={isExpanded}
>
@@ -131,7 +131,7 @@ const MobileAccordionMenu = ({ onClose }: MobileAccordionMenuProps) => {
onClick={onClose}
className={`p-chorus-sm flex items-center gap-2 px-chorus-sm transition-all duration-300 ease-out hover:bg-sand-50 hover:bg-opacity-30 hover:text-mulberry-950 dark:hover:bg-mulberry-900 dark:hover:bg-opacity-40 dark:hover:text-white ${
isCurrentPage(item.href)
? 'bg-sand-50 text-mulberry-950 dark:bg-mulberry-900 dark:text-white font-medium border-l-2 border-mulberry-600 dark:border-mulberry-400'
? 'transparent text-mulberry-950 dark:text-white font-medium border-l-2 border-mulberry-600 dark:border-mulberry-400'
: 'text-carbon-600 dark:text-mulberry-400'
}`}
>
@@ -266,7 +266,7 @@ const Header = () => {
{/* Desktop primary navigation - hidden on mobile */}
<nav className="primary-nav hidden lg:block">
<ul className="horizontal-menu">
<li className="dark:hover:bg-mulberry-700 hover:bg-sand-300 backdrop-blur">
<li className="item">
<div className="flex items-center gap-2 ml-chorus-sm">
<img src="/icons/coolicons.v4.1/coolicons PNG/Black/Navigation/House_01.png" alt="" className="icon py-chous-sm h-4 w-4 dark:hidden" />
<img src="/icons/coolicons.v4.1/coolicons PNG/White/Navigation/House_01.png" alt="" className="icon py-chous-sm hidden h-4 w-4 dark:inline-flex" />
@@ -296,7 +296,7 @@ const Header = () => {
</li>
</ul>
</li>
<li className="dark:hover:bg-mulberry-700 hover:bg-sand-300 backdrop-blur">
<li className="item">
<div className="flex items-center gap-2 ml-chorus-sm">
<img src="/icons/coolicons.v4.1/coolicons PNG/Black/Shape/Circle.png" alt="" className="icon py-chous-sm h-4 w-4 dark:hidden" />
<img src="/icons/coolicons.v4.1/coolicons PNG/White/Shape/Circle.png" alt="" className="icon py-chous-sm hidden h-4 w-4 dark:inline-flex" />
@@ -348,15 +348,15 @@ const Header = () => {
</li>
<li className="item">
<Link href="/components" className="dark:hover:bg-mulberry-700 hover:bg-sand-300 transition-all duration-300 ease-out flex items-center gap-2">
<img src="/icons/coolicons.v4.1/coolicons PNG/Black/Interface/Settings.png" alt="" className="icon py-chous-sm h-3 w-3 dark:hidden" />
<img src="/icons/coolicons.v4.1/coolicons PNG/White/Interface/Settings.png" alt="" className="icon py-chous-sm hidden h-3 w-3 dark:inline-flex" />
<img src="/icons/coolicons.v4.1/coolicons PNG/Black/Environment/Puzzle.png" alt="" className="icon py-chous-sm h-3 w-3 dark:hidden" />
<img src="/icons/coolicons.v4.1/coolicons PNG/White/Environment/Puzzle.png" alt="" className="icon py-chous-sm hidden h-3 w-3 dark:inline-flex" />
<span>Components</span>
</Link>
</li>
<li className="item">
<Link href="/motion" className="dark:hover:bg-mulberry-700 hover:bg-sand-300 transition-all duration-300 ease-out flex items-center gap-2">
<img src="/icons/coolicons.v4.1/coolicons PNG/Black/Interface/Link.png" alt="" className="icon py-chous-sm h-3 w-3 dark:hidden" />
<img src="/icons/coolicons.v4.1/coolicons PNG/White/Interface/Link.png" alt="" className="icon py-chous-sm hidden h-3 w-3 dark:inline-flex" />
<img src="/icons/coolicons.v4.1/coolicons PNG/Black/Interface/Trending_Up.png" alt="" className="icon py-chous-sm h-3 w-3 dark:hidden" />
<img src="/icons/coolicons.v4.1/coolicons PNG/White/Interface/Trending_Up.png" alt="" className="icon py-chous-sm hidden h-3 w-3 dark:inline-flex" />
<span>Motion System</span>
</Link>
</li>
@@ -369,7 +369,7 @@ const Header = () => {
</li>
</ul>
</li>
<li className="dark:hover:bg-mulberry-700 hover:bg-sand-300">
<li className="item">
<div className="flex items-center gap-2 ml-chorus-sm">
<img src="/icons/coolicons.v4.1/coolicons PNG/Black/Communication/Chat.png" alt="" className="icon py-chous-sm h-4 w-4 dark:hidden" />
<img src="/icons/coolicons.v4.1/coolicons PNG/White/Communication/Chat.png" alt="" className="icon py-chous-sm hidden h-4 w-4 dark:inline-flex" />

View File

@@ -0,0 +1,62 @@
'use client';
import React from 'react';
import { useIntersectionObserver } from '@/hooks/useIntersectionObserver';
interface ScrollRevealProps {
children: React.ReactNode;
delay?: number;
duration?: number;
direction?: 'up' | 'down' | 'left' | 'right';
distance?: number;
className?: string;
}
export default function ScrollReveal({
children,
delay = 0,
duration = 600,
direction = 'up',
distance = 24,
className = '',
}: ScrollRevealProps) {
const { elementRef, isVisible } = useIntersectionObserver({
threshold: 0.1,
rootMargin: '0px 0px -100px 0px',
triggerOnce: true,
});
const getTransform = (visible: boolean) => {
if (visible) return 'translate3d(0, 0, 0)';
switch (direction) {
case 'up':
return `translate3d(0, ${distance}px, 0)`;
case 'down':
return `translate3d(0, -${distance}px, 0)`;
case 'left':
return `translate3d(${distance}px, 0, 0)`;
case 'right':
return `translate3d(-${distance}px, 0, 0)`;
default:
return `translate3d(0, ${distance}px, 0)`;
}
};
const revealStyle = {
opacity: isVisible ? 1 : 0,
transform: getTransform(isVisible),
transition: `opacity ${duration}ms ease-out ${delay}ms, transform ${duration}ms ease-out ${delay}ms`,
willChange: 'opacity, transform',
};
return (
<div
ref={elementRef}
style={revealStyle}
className={`scroll-reveal ${className}`}
>
{children}
</div>
);
}

View File

@@ -22,7 +22,7 @@ export default function ThreeLogo({ width = 64, height = 64, className = "" }: T
// Scene / Renderer / Camera
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
camera.position.set(0, 0, 2.2); // Move camera back to prevent clipping
camera.position.set(0, 0, 3.0); // Move camera back to prevent clipping
camera.lookAt(0, 0, 0); // Ensure camera looks at exact center
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });

View File

@@ -0,0 +1,49 @@
'use client';
import { useEffect, useRef, useState } from 'react';
interface IntersectionObserverOptions {
threshold?: number;
rootMargin?: string;
triggerOnce?: boolean;
}
export function useIntersectionObserver(
options: IntersectionObserverOptions = {}
) {
const { threshold = 0.1, rootMargin = '0px 0px -100px 0px', triggerOnce = true } = options;
const [isIntersecting, setIsIntersecting] = useState(false);
const [hasIntersected, setHasIntersected] = useState(false);
const elementRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const element = elementRef.current;
if (!element) return;
const observer = new IntersectionObserver(
([entry]) => {
const isVisible = entry.isIntersecting;
setIsIntersecting(isVisible);
if (isVisible && !hasIntersected) {
setHasIntersected(true);
}
},
{
threshold,
rootMargin,
}
);
observer.observe(element);
return () => {
observer.unobserve(element);
};
}, [threshold, rootMargin, hasIntersected]);
return {
elementRef,
isVisible: triggerOnce ? hasIntersected : isIntersecting,
};
}