From b3cfcfe057f304de309942d0387e289d1f216cfe Mon Sep 17 00:00:00 2001 From: Amirhossein Khalili Date: Wed, 9 Sep 2026 17:53:48 +0330 Subject: [PATCH] fix: stabilize portfolio scrolling and loading Remove skeleton swaps and deferred layout estimates, restore reveal state, and use native proximity scrolling for project cards. Also make the Docker build npm registry configurable. --- Dockerfile | 6 +- src/app/(en)/loading.tsx | 5 - src/app/(fa)/fa/loading.tsx | 5 - src/app/PortfolioPage.tsx | 14 +-- src/components/EducationSection.tsx | 7 +- src/components/ExperienceSection.tsx | 8 +- src/components/FooterSection.tsx | 2 +- src/components/HeroSection.module.css | 9 -- src/components/HeroSection.tsx | 5 +- src/components/ImageWithSkeleton.module.css | 73 ----------- src/components/ImageWithSkeleton.tsx | 113 ----------------- .../IsometricFooter.module.css | 28 ----- .../IsometricFooter/IsometricFooter.tsx | 6 +- src/components/MilestonesSection.tsx | 7 +- src/components/MotionController.tsx | 37 ++++-- src/components/ProjectThumbnail.tsx | 6 +- src/components/ProjectsSection.module.css | 8 +- src/components/ProjectsSection.tsx | 116 ++++-------------- src/components/SkillsSection.tsx | 7 +- src/components/scenes/AboutRiverScene.tsx | 4 +- .../scenes/DeferredAboutRiverScene.tsx | 6 +- src/components/scenes/Scenes.module.css | 22 ---- src/components/scenes/ThreeCanvas.tsx | 13 +- .../skeletons/PortfolioSkeleton.module.css | 66 ---------- .../skeletons/PortfolioSkeleton.tsx | 48 -------- src/styles/globals.css | 39 ------ tests/scroll-and-loading.test.mjs | 87 +++++++++++++ 27 files changed, 180 insertions(+), 567 deletions(-) delete mode 100644 src/app/(en)/loading.tsx delete mode 100644 src/app/(fa)/fa/loading.tsx delete mode 100644 src/components/ImageWithSkeleton.module.css delete mode 100644 src/components/ImageWithSkeleton.tsx delete mode 100644 src/components/skeletons/PortfolioSkeleton.module.css delete mode 100644 src/components/skeletons/PortfolioSkeleton.tsx create mode 100644 tests/scroll-and-loading.test.mjs diff --git a/Dockerfile b/Dockerfile index 6ea8877..cab7d75 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,8 +2,12 @@ FROM node:22-alpine AS builder WORKDIR /app +ARG NPM_REGISTRY=https://package-mirror.liara.ir/repository/npm/ + COPY package.json package-lock.json ./ -RUN npm ci +RUN npm ci \ + --registry="${NPM_REGISTRY}" \ + --replace-registry-host=registry.npmjs.org COPY . . RUN npm run build diff --git a/src/app/(en)/loading.tsx b/src/app/(en)/loading.tsx deleted file mode 100644 index 9706c32..0000000 --- a/src/app/(en)/loading.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { PortfolioSkeleton } from '@/components/skeletons/PortfolioSkeleton'; - -export default function Loading() { - return ; -} diff --git a/src/app/(fa)/fa/loading.tsx b/src/app/(fa)/fa/loading.tsx deleted file mode 100644 index 9706c32..0000000 --- a/src/app/(fa)/fa/loading.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import { PortfolioSkeleton } from '@/components/skeletons/PortfolioSkeleton'; - -export default function Loading() { - return ; -} diff --git a/src/app/PortfolioPage.tsx b/src/app/PortfolioPage.tsx index b1da4a7..2d54ae7 100644 --- a/src/app/PortfolioPage.tsx +++ b/src/app/PortfolioPage.tsx @@ -1,4 +1,3 @@ -import { Suspense } from 'react'; import { ScenicHeroJourney } from '@/components/ScenicHeroJourney'; import { ProjectsSection } from '@/components/ProjectsSection'; import { ExperienceSection } from '@/components/ExperienceSection'; @@ -7,7 +6,6 @@ import { EducationSection } from '@/components/EducationSection'; import { MilestonesSection } from '@/components/MilestonesSection'; import { FooterSection } from '@/components/FooterSection'; import { MotionController } from '@/components/MotionController'; -import { SectionSkeleton } from '@/components/skeletons/PortfolioSkeleton'; import type { Locale } from '@/i18n/content'; export function PortfolioPage({ locale }: { locale: Locale }) { @@ -15,12 +13,12 @@ export function PortfolioPage({ locale }: { locale: Locale }) {
- }> - }> - }> - }> - }> - }> + + + + + +
); } diff --git a/src/components/EducationSection.tsx b/src/components/EducationSection.tsx index 2881e14..10099b9 100644 --- a/src/components/EducationSection.tsx +++ b/src/components/EducationSection.tsx @@ -1,4 +1,4 @@ -import { ImageWithSkeleton } from './ImageWithSkeleton'; +import Image from 'next/image'; import { getEducation } from '@/data/education'; import { messagesByLocale, type Locale } from '@/i18n/content'; import styles from './EducationSection.module.css'; @@ -8,7 +8,7 @@ export const EducationSection = ({ locale }: { locale: Locale }) => { const copy = messagesByLocale[locale].education; return ( -
+
@@ -21,13 +21,12 @@ export const EducationSection = ({ locale }: { locale: Locale }) => {
-
diff --git a/src/components/ExperienceSection.tsx b/src/components/ExperienceSection.tsx index 9c47646..ebe6e79 100644 --- a/src/components/ExperienceSection.tsx +++ b/src/components/ExperienceSection.tsx @@ -1,4 +1,4 @@ -import { ImageWithSkeleton } from './ImageWithSkeleton'; +import Image from 'next/image'; import { getExperiences } from '@/data/experience'; import { messagesByLocale, type Locale } from '@/i18n/content'; import styles from './ExperienceSection.module.css'; @@ -8,7 +8,7 @@ export const ExperienceSection = ({ locale }: { locale: Locale }) => { const copy = messagesByLocale[locale].experience; return ( -
+
@@ -31,6 +31,7 @@ export const ExperienceSection = ({ locale }: { locale: Locale }) => { className={`${styles.nodeDot} ${ item.isCurrent ? styles.nodeCurrent : '' }`} + data-looping-motion={item.isCurrent ? '' : undefined} aria-hidden="true" /> {index < experiences.length - 1 && ( @@ -65,13 +66,12 @@ export const ExperienceSection = ({ locale }: { locale: Locale }) => {
-
diff --git a/src/components/FooterSection.tsx b/src/components/FooterSection.tsx index ac530b4..c3f95fc 100644 --- a/src/components/FooterSection.tsx +++ b/src/components/FooterSection.tsx @@ -4,7 +4,7 @@ import styles from './FooterSection.module.css'; export const FooterSection = ({ locale }: { locale: Locale }) => { return ( -