perf: optimize portfolio runtime delivery
Some checks failed
Test and deploy / Verify production build (push) Has been cancelled
Test and deploy / Deploy production (push) Has been cancelled

Replace the blocking asset loader with progressive rendering, defer expensive media and Three.js work, and reduce hydration and animation costs. Move deployment from a static Nginx export to the standalone Next.js runtime with optimized dynamic images.
This commit is contained in:
2026-09-09 12:43:37 +03:30
parent 9143ba8504
commit f34bea05cd
63 changed files with 1296 additions and 1048 deletions

View File

@@ -1,208 +1,52 @@
'use client';
import { useEffect, useRef, useState } from 'react';
import Image from 'next/image';
import Link from 'next/link';
import {
BookOpenText,
Check,
GraduationCap,
Languages,
MessageCircle,
Moon,
Sun,
} from 'lucide-react';
import { useTheme } from '@/context/ThemeContext';
import { BookOpenText, GraduationCap, MessageCircle } from 'lucide-react';
import { messagesByLocale, type Locale } from '@/i18n/content';
import { HeroControls } from './HeroControls';
import { ImageWithSkeleton } from './ImageWithSkeleton';
import styles from './HeroSection.module.css';
export const HeroSection = ({ locale }: { locale: Locale }) => {
const { theme, toggleTheme } = useTheme();
const copy = messagesByLocale[locale].hero;
const themeLabel = theme === 'light' ? copy.themeDarkLabel : copy.themeLightLabel;
const [isLanguageMenuOpen, setIsLanguageMenuOpen] = useState(false);
const languageMenuRef = useRef<HTMLDivElement>(null);
const languageButtonRef = useRef<HTMLButtonElement>(null);
useEffect(() => {
if (!isLanguageMenuOpen) return;
const closeOnOutsideClick = (event: PointerEvent) => {
if (!languageMenuRef.current?.contains(event.target as Node)) {
setIsLanguageMenuOpen(false);
}
};
const closeOnEscape = (event: KeyboardEvent) => {
if (event.key !== 'Escape') return;
setIsLanguageMenuOpen(false);
languageButtonRef.current?.focus();
};
document.addEventListener('pointerdown', closeOnOutsideClick);
document.addEventListener('keydown', closeOnEscape);
return () => {
document.removeEventListener('pointerdown', closeOnOutsideClick);
document.removeEventListener('keydown', closeOnEscape);
};
}, [isLanguageMenuOpen]);
return (
<div className={styles.heroContentWrapper}>
{/* Top utility control */}
<div className={styles.topBar}>
<button
type="button"
onClick={toggleTheme}
className={styles.iconButton}
aria-label={themeLabel}
title={themeLabel}
>
{theme === 'light' ? (
<Moon size={28} strokeWidth={1.8} className={styles.themeIcon} />
) : (
<Sun size={28} strokeWidth={1.8} className={styles.controlIcon} />
)}
</button>
<div className={styles.languageMenuWrapper} ref={languageMenuRef}>
<button
ref={languageButtonRef}
type="button"
className={`${styles.iconButton} ${styles.languageButton}`}
aria-label={copy.languageButtonLabel}
title={copy.languageButtonLabel}
aria-haspopup="menu"
aria-expanded={isLanguageMenuOpen}
aria-controls="language-menu"
onClick={() => setIsLanguageMenuOpen((isOpen) => !isOpen)}
data-language-switch
>
<Languages size={23} strokeWidth={1.9} aria-hidden="true" />
</button>
<div
id="language-menu"
className={styles.languageMenu}
role="menu"
aria-label={copy.languageMenuLabel}
data-open={isLanguageMenuOpen}
>
<Link
href="/"
hrefLang="en"
role="menuitem"
aria-current={locale === 'en' ? 'page' : undefined}
className={styles.languageOption}
>
<span className={styles.languageOptionLabel}>
<Image
src="/images/flags/english.svg"
alt=""
width={20}
height={20}
className={styles.languageFlag}
aria-hidden="true"
/>
<span lang="en" dir="ltr">{copy.englishLabel}</span>
</span>
{locale === 'en' && (
<Check
size={16}
className={styles.selectedIcon}
aria-hidden="true"
/>
)}
</Link>
<Link
href="/fa"
hrefLang="fa"
role="menuitem"
aria-current={locale === 'fa' ? 'page' : undefined}
className={styles.languageOption}
>
{locale === 'fa' && (
<Check
size={16}
className={styles.selectedIcon}
aria-hidden="true"
/>
)}
<span className={styles.languageOptionLabel}>
<Image
src="/images/flags/persian.svg"
alt=""
width={20}
height={20}
className={styles.languageFlag}
aria-hidden="true"
/>
<span lang="fa" dir="rtl">{copy.persianLabel}</span>
</span>
</Link>
</div>
</div>
</div>
{/* Hero Central Content */}
<HeroControls locale={locale} />
<div className={styles.heroContent}>
{/* Avatar with double ring */}
<div className={styles.avatarWrapper}>
<div className={styles.avatarRingOuter}>
<div className={styles.avatarContainer}>
<Image
<ImageWithSkeleton
src="/images/avatar.png"
alt={copy.avatarAlt}
width={192}
height={192}
fill
sizes="192px"
preload
skeletonShape="circle"
className={styles.avatarImg}
priority
/>
</div>
</div>
</div>
{/* Display Typography */}
<h1 className={`font-display ${styles.heroTitle}`} aria-label={copy.titleLabel}>
<span className={styles.heroGreeting}>{copy.greeting}</span>
<span className={styles.heroName}>{copy.name}</span>
</h1>
<p className={styles.heroSubtitle}>{copy.subtitle}</p>
{/* Action Buttons */}
<div className={styles.actionButtons}>
<button
type="button"
className={`pill-btn ${styles.heroButton} ${styles.heroButtonUpcoming}`}
aria-label={copy.blogLabel}
title={copy.comingSoon}
disabled
>
<button type="button" className={`pill-btn ${styles.heroButton} ${styles.heroButtonUpcoming}`} aria-label={copy.blogLabel} title={copy.comingSoon} disabled>
<BookOpenText size={17} strokeWidth={2} aria-hidden="true" />
{copy.blog}
</button>
<button
type="button"
className={`pill-btn ${styles.heroButton} ${styles.heroButtonUpcoming}`}
aria-label={copy.coursesLabel}
title={copy.comingSoon}
disabled
>
<button type="button" className={`pill-btn ${styles.heroButton} ${styles.heroButtonUpcoming}`} aria-label={copy.coursesLabel} title={copy.comingSoon} disabled>
<GraduationCap size={18} strokeWidth={2} aria-hidden="true" />
{copy.courses}
</button>
<a
href="https://www.linkedin.com/messaging/compose/?recipient=ACoAAEJsAwMBCv3L9dYcEXWam-l3pLOJ3UspX7w"
target="_blank"
rel="noreferrer"
className={`pill-btn ${styles.heroButton} ${styles.heroButtonPrimary}`}
aria-label={copy.contactLabel}
>
<a href="https://www.linkedin.com/messaging/compose/?recipient=ACoAAEJsAwMBCv3L9dYcEXWam-l3pLOJ3UspX7w" target="_blank" rel="noreferrer" className={`pill-btn ${styles.heroButton} ${styles.heroButtonPrimary}`} aria-label={copy.contactLabel}>
<MessageCircle size={17} strokeWidth={2} aria-hidden="true" />
{copy.contact}
</a>
</div>
</div>
</div>
);