Skip to content

Commit 71594e9

Browse files
Update: refactor codebase structure and configurations
1 parent ab20d75 commit 71594e9

28 files changed

Lines changed: 274 additions & 175 deletions

File tree

index.css

Lines changed: 88 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@
4242
--fallback-border: #ffecb5;
4343

4444
/* Theme transition timing for smooth switching */
45-
--theme-transition-duration: 0.4s;
46-
--theme-transition-timing: cubic-bezier(0.4, 0.0, 0.2, 1);
45+
--theme-transition-duration: 0.3s;
46+
--theme-transition-timing: ease-out;
47+
--theme-transition-timing-smooth: ease-out;
4748

4849
/* Liquid Glass Effect Variables */
4950
--liquid-glass-edge-size: 20px;
@@ -92,7 +93,8 @@ html {
9293
scroll-behavior: smooth;
9394
scroll-padding-top: calc(var(--nav-height) + 20px); /* Match the offset in smoothScrollTo function */
9495
transition:
95-
background-color var(--theme-transition-duration) var(--theme-transition-timing)}
96+
background-color var(--theme-transition-duration) var(--theme-transition-timing-smooth),
97+
color-scheme var(--theme-transition-duration) var(--theme-transition-timing-smooth)}
9698

9799
body {
98100
font-family: var(--font-body);
@@ -103,8 +105,8 @@ body {
103105
-moz-osx-font-smoothing: grayscale;
104106
min-height: 100vh;
105107
transition:
106-
background-color var(--theme-transition-duration) var(--theme-transition-timing),
107-
color var(--theme-transition-duration) var(--theme-transition-timing);
108+
background-color var(--theme-transition-duration) var(--theme-transition-timing-smooth),
109+
color var(--theme-transition-duration) var(--theme-transition-timing-smooth);
108110
overflow-x: hidden;
109111
position: relative}
110112
body.mobile-menu-open {
@@ -247,8 +249,8 @@ html[data-theme="light"] .btn-outline:hover, html[data-theme="light"] .btn-outli
247249

248250
.navbar { position: fixed; top: 0; left: 0; width: 100%; height: var(--nav-height); background-color: var(--nav-bg); backdrop-filter: blur(var(--blur-strength)); -webkit-backdrop-filter: blur(var(--blur-strength)); display: flex; align-items: center; padding: 0 20px; z-index: 1000; border-bottom: 1px solid var(--glass-border-color); transition:
249251
top 0.3s ease,
250-
background-color var(--theme-transition-duration) var(--theme-transition-timing),
251-
border-color var(--theme-transition-duration) var(--theme-transition-timing),
252+
background-color var(--theme-transition-duration) var(--theme-transition-timing-smooth),
253+
border-color var(--theme-transition-duration) var(--theme-transition-timing-smooth),
252254
box-shadow var(--transition-speed);
253255
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1)}
254256

@@ -2707,6 +2709,40 @@ section {
27072709
}
27082710

27092711
/* Theme transition overlay for ultra-smooth experience */
2712+
/* View Transition API Support for ultra-smooth theme switching */
2713+
::view-transition-old(root),
2714+
::view-transition-new(root) {
2715+
animation-duration: 0.35s;
2716+
animation-timing-function: cubic-bezier(0.32, 0.08, 0.24, 1);
2717+
}
2718+
2719+
::view-transition-old(root) {
2720+
animation-name: theme-fade-out;
2721+
}
2722+
2723+
::view-transition-new(root) {
2724+
animation-name: theme-fade-in;
2725+
}
2726+
2727+
@keyframes theme-fade-out {
2728+
from {
2729+
opacity: 1;
2730+
}
2731+
to {
2732+
opacity: 0;
2733+
}
2734+
}
2735+
2736+
@keyframes theme-fade-in {
2737+
from {
2738+
opacity: 0;
2739+
}
2740+
to {
2741+
opacity: 1;
2742+
}
2743+
}
2744+
2745+
/* Smooth overlay for theme transitions */
27102746
body::after {
27112747
content: '';
27122748
position: fixed;
@@ -2718,35 +2754,39 @@ body::after {
27182754
opacity: 0;
27192755
z-index: 9999;
27202756
pointer-events: none;
2721-
transition:
2722-
opacity 0.2s ease-out,
2723-
background-color var(--theme-transition-duration) var(--theme-transition-timing);
2724-
will-change: opacity;
2757+
transition: opacity 0.15s ease-in-out;
27252758
}
27262759

27272760
body.theme-transitioning::after {
2728-
opacity: 0.08;
2761+
opacity: 0.2;
27292762
}
27302763

2731-
/* Prevent layout shift during theme transition */
2764+
/* Prevent layout shift and optimize during theme transition */
27322765
body.theme-transitioning {
27332766
overflow-x: hidden;
27342767
}
27352768

2736-
/* Smooth color transitions for all themed elements */
2737-
* {
2738-
transition-property: background-color, border-color, color, fill, stroke;
2739-
transition-duration: var(--theme-transition-duration);
2740-
transition-timing-function: var(--theme-transition-timing);
2769+
body.theme-transitioning * {
2770+
animation-play-state: paused !important;
27412771
}
27422772

2743-
/* Exclude specific elements from theme transitions */
2744-
.animate-in,
2745-
.glass-panel,
2746-
.modal-overlay,
2747-
.modal-content,
2748-
canvas {
2749-
transition-property: background-color, border-color, color, fill, stroke, transform, opacity, box-shadow !important;
2773+
/* Smooth color transitions for themed elements */
2774+
body,
2775+
.navbar,
2776+
.section,
2777+
.glass-card,
2778+
.btn,
2779+
p,
2780+
h1, h2, h3, h4, h5, h6,
2781+
.nav-logo,
2782+
.nav-links a,
2783+
.project-card,
2784+
.skill-item,
2785+
footer {
2786+
transition: background-color var(--theme-transition-duration) ease-out,
2787+
color var(--theme-transition-duration) ease-out,
2788+
border-color var(--theme-transition-duration) ease-out,
2789+
box-shadow var(--theme-transition-duration) ease-out;
27502790
}
27512791

27522792
/* Global scrollbar transitions moved to scrollbar.css */
@@ -2765,8 +2805,13 @@ canvas {
27652805

27662806
/* Theme toggle enhanced animations */
27672807
.theme-toggle-btn, .theme-toggle {
2768-
transition: all 0.3s cubic-bezier(0.4, 0.0, 0.2, 1);
2769-
transform-origin: center}
2808+
transition: transform 0.2s ease-out;
2809+
transform-origin: center;
2810+
}
2811+
2812+
.theme-toggle-btn:active {
2813+
transform: scale(0.95);
2814+
}
27702815

27712816
.theme-toggle:hover {
27722817
transform: scale(1.1) rotate(10deg)}
@@ -2831,9 +2876,25 @@ canvas {
28312876
.theme-toggle:hover {
28322877
transform: none}
28332878

2879+
.theme-toggle-btn:active {
2880+
transform: none !important}
2881+
2882+
.theme-toggle-btn svg {
2883+
transition: none !important}
2884+
2885+
.theme-transitioning .theme-toggle-btn svg {
2886+
transform: none !important}
2887+
28342888
.btn:hover {
28352889
transform: none}
28362890

2891+
body::after {
2892+
display: none !important}
2893+
2894+
::view-transition-old(root),
2895+
::view-transition-new(root) {
2896+
animation: none !important}
2897+
28372898
.nav-links a {
28382899
filter: none !important;
28392900
backdrop-filter: blur(10px) !important}

index.html

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,8 @@
126126
<link rel="preconnect" href="https://www.googletagmanager.com">
127127

128128
<!-- Preload critical resources -->
129-
<link rel="modulepreload" href="/src/shared/wasm/portfolio_engine.js">
130-
<link rel="preload" href="/src/shared/wasm/portfolio_engine_bg.wasm" as="fetch" crossorigin>
131-
<link rel="preload" href="/asset/profile/profile-photo.jpg" as="image" type="image/jpeg">
129+
<!-- Note: Vite handles modulepreload automatically in production builds -->
130+
<link rel="preload" href="/asset/profile/profile-photo.jpg" as="image" type="image/jpeg" fetchpriority="high">
132131
<!-- Critical CSS - inline for faster initial render -->
133132
<style>
134133
/* Critical above-the-fold styles */

index.tsx

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,24 +27,25 @@ if (typeof window !== 'undefined' && process.env.NODE_ENV === 'production') {
2727

2828
import '@/i18n';
2929
import { useTranslation, CertificateItem } from '@/features/i18n';
30-
import { useGeminiConnectionCheck } from '@/features/chatbot';
30+
import { useGeminiConnectionCheck } from '@/features/chatbot/hooks/useGeminiConnection';
3131
import { useChatbotLoader } from '@/features/chatbot/hooks/useChatbotLoader';
3232
import { useReducedMotion, useAnnouncer } from '@/shared/hooks';
33-
import {
34-
DynamicContentProvider,
35-
ProfileInsights,
36-
SHOW_VISITOR_CONTROLS,
37-
SHOW_PROFILE_INSIGHTS,
38-
SHOW_TRANSLATION_DEBUG,
39-
SHOW_DEBUG_INFO,
40-
IS_DEVELOPMENT,
41-
SHOW_RECOMMENDED_SECTIONS
42-
} from '@/features/visitor-personalization';
33+
import { DynamicContentProvider, ProfileInsights } from '@/features/visitor-personalization';
4334
import { analytics } from '@/features/analytics';
44-
import { PERSONAS_FEATURE_ENABLED, getSectionIds } from '@/shared/config';
45-
import { AnimationPauseProvider, SimpleConsentProvider } from '@/core';
35+
import {
36+
PERSONAS_FEATURE_ENABLED,
37+
getSectionIds,
38+
SHOW_VISITOR_CONTROLS,
39+
SHOW_PROFILE_INSIGHTS,
40+
SHOW_TRANSLATION_DEBUG,
41+
SHOW_DEBUG_INFO,
42+
IS_DEVELOPMENT,
43+
SHOW_RECOMMENDED_SECTIONS
44+
} from '@/shared/config';
45+
import { AnimationPauseProvider, SimpleConsentProvider, useAnimationPauseControl } from '@/core';
4646
import { Navbar, SkipLinks, SEOHead, EnhancedLoadingScreen } from '@/shared/components';
47-
import { performanceLogger, LazyTranslationTest, LazyThreeBackground, loadingManager, useLoadingManager } from '@/shared/utils';
47+
import { performanceLogger, LazyThreeBackground, loadingManager, useLoadingManager } from '@/shared/utils';
48+
import { LazyTranslationTest } from '@/shared/utils/lazyLoading'; // Direct import to avoid circular dependency
4849
import { ANIMATION_DURATION, SCROLL, OBSERVER_CONFIG } from '@/shared/constants';
4950
import {
5051
HomeSection,
@@ -58,7 +59,7 @@ import {
5859
} from '@/features/portfolio';
5960

6061
// Lazy load heavy components with preloading hints
61-
const Chatbot = lazy(() => import('@/shared/components').then(m => ({ default: m.Chatbot })));
62+
const Chatbot = lazy(() => import('@/features/chatbot/components/Chatbot').then(m => ({ default: m.Chatbot })));
6263
const CertificateModal = lazy(() => import('@/features/portfolio').then(m => ({ default: m.CertificateModal })));
6364
const ProjectsSection = lazy(() => import('@/features/portfolio').then(m => ({ default: m.ProjectsSection })));
6465
const PublicationsSection = lazy(() => import('@/features/portfolio').then(m => ({ default: m.PublicationsSection })));
@@ -115,6 +116,7 @@ const App: React.FC = () => {
115116
const [isThemeTransitioning, setIsThemeTransitioning] = useState(false);
116117
const [showBackground] = useState(true);
117118
const reducedMotion = useReducedMotion();
119+
const { setPaused } = useAnimationPauseControl();
118120

119121
// Lazy load chatbot on user interaction
120122
const { isLoaded: isChatbotLoaded, isLoading: isChatbotLoading, error: chatbotError } = useChatbotLoader();
@@ -337,18 +339,43 @@ const App: React.FC = () => {
337339
if (isThemeTransitioning) return;
338340

339341
setIsThemeTransitioning(true);
342+
343+
// Pause particle animations immediately for smooth transition
344+
setPaused(true);
345+
340346
const newTheme = theme === 'light' ? 'dark' : 'light';
341347

342-
// Use RAF to ensure smooth transition
343-
requestAnimationFrame(() => {
344-
setTheme(newTheme);
345-
346-
// Announce theme change to screen readers
347-
const message = String(newTheme === 'light'
348-
? t('theme.changedToLight')
349-
: t('theme.changedToDark'));
350-
announce(message, 'polite');
351-
});
348+
// Use View Transition API for smooth theme switching (with fallback)
349+
const updateTheme = () => {
350+
// Small delay to let the overlay appear
351+
setTimeout(() => {
352+
setTheme(newTheme);
353+
354+
// Announce theme change to screen readers
355+
const message = String(newTheme === 'light'
356+
? t('theme.changedToLight')
357+
: t('theme.changedToDark'));
358+
announce(message, 'polite');
359+
}, 50); // 50ms delay for smooth overlay appearance
360+
};
361+
362+
// Check if View Transition API is supported
363+
if ('startViewTransition' in document && !reducedMotion) {
364+
// Use native browser View Transition API for ultra-smooth transitions
365+
(document as any).startViewTransition(() => {
366+
updateTheme();
367+
});
368+
} else {
369+
// Fallback for browsers without View Transition API
370+
requestAnimationFrame(() => {
371+
updateTheme();
372+
});
373+
}
374+
375+
// Resume particles after transition completes with smooth fade-in
376+
setTimeout(() => {
377+
setPaused(false);
378+
}, 400); // Resume after transition
352379
};
353380

354381
useEffect(() => {

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"scripts": {
1212
"dev": "vite",
1313
"build": "vite build && node scripts/copy-assets.js && node scripts/generate-sitemap.js",
14+
"build:dev": "vite build --mode development && node scripts/copy-assets.js && node scripts/generate-sitemap.js",
1415
"build:prod": "vite build --mode production && node scripts/copy-assets.js && node scripts/generate-sitemap.js",
1516
"build:prod:debug": "VITE_NO_MINIFY=true vite build --mode production && node scripts/copy-assets.js && node scripts/generate-sitemap.js",
1617
"build:analyze": "vite build && open dist/bundle-analysis.html",

0 commit comments

Comments
 (0)