From 31a52fed2dff704474cf9677bd978de3938600a8 Mon Sep 17 00:00:00 2001 From: Amirhossein Khalili Date: Sun, 6 Sep 2026 21:59:20 +0330 Subject: [PATCH] fix(projects): improve carousel controls and case study actions --- src/components/ProjectsSection.module.css | 78 ++++----------- src/components/ProjectsSection.tsx | 112 +++++++++++----------- 2 files changed, 75 insertions(+), 115 deletions(-) diff --git a/src/components/ProjectsSection.module.css b/src/components/ProjectsSection.module.css index b12247d..8a3d376 100644 --- a/src/components/ProjectsSection.module.css +++ b/src/components/ProjectsSection.module.css @@ -52,6 +52,7 @@ .navButton:disabled { cursor: default; + opacity: 0.36; } [data-theme="dark"] .navButtonPrevious { @@ -115,7 +116,7 @@ box-sizing: border-box; border: 1px solid var(--border-default); border-radius: var(--radius-lg); - background: var(--bg-surface); + background: var(--bg-raised); scroll-snap-align: start; transition: border-color 0.25s ease, box-shadow 0.25s ease, transform 0.25s ease; } @@ -225,6 +226,7 @@ font-size: var(--font-size-body-small); font-weight: 600; transition: color 0.2s ease, gap 0.2s ease; + min-height: 2.75rem; } .caseStudyLink:focus-visible { @@ -243,67 +245,29 @@ margin-top: 2.5rem; } -.scrollbar { - --thumb-width: 13.5rem; +.progressTrack { + position: relative; + overflow: hidden; width: 30rem; max-width: 100%; - height: 1.25rem; - margin: 0; - appearance: none; - background: transparent; - cursor: pointer; -} - -.scrollbar::-webkit-slider-runnable-track { height: 0.375rem; border-radius: var(--radius-pill); background: var(--color-blue-200); } -.scrollbar::-webkit-slider-thumb { - width: var(--thumb-width, 13.5rem); - height: 0.375rem; - margin-top: 0; - appearance: none; - border: 0; +.progressValue { + display: block; + height: 100%; border-radius: var(--radius-pill); background: var(--color-blue-400); + transition: width var(--motion-duration-default) var(--motion-ease-out); } -.scrollbar::-moz-range-track { - height: 0.375rem; - border: 0; - border-radius: var(--radius-pill); - background: var(--color-blue-200); -} - -.scrollbar::-moz-range-thumb { - width: var(--thumb-width, 13.5rem); - height: 0.375rem; - border: 0; - border-radius: var(--radius-pill); - background: var(--color-blue-400); -} - -.scrollbar:focus-visible { - border-radius: var(--radius-pill); - outline: 3px solid var(--focus-ring); - outline-offset: 3px; -} - -[data-theme="dark"] .scrollbar::-webkit-slider-runnable-track { +[data-theme="dark"] .progressTrack { background: var(--color-blue-800); } -[data-theme="dark"] .scrollbar::-webkit-slider-thumb { - background: var(--color-blue-300); -} - -[data-theme="dark"] .scrollbar::-moz-range-track { - background: var(--color-blue-800); -} - -[data-theme="dark"] .scrollbar::-moz-range-thumb { +[data-theme="dark"] .progressValue { background: var(--color-blue-300); } @@ -321,9 +285,8 @@ width: clamp(18.125rem, 23vw, 20.625rem); } - .scrollbar { + .progressTrack { width: min(30rem, 50vw); - --thumb-width: 7.5rem; } } @@ -398,9 +361,8 @@ margin-top: 2rem; } - .scrollbar { + .progressTrack { width: min(22.5rem, 60vw); - --thumb-width: 5.625rem; } } @@ -463,8 +425,8 @@ } .cardDesc { - font-size: 0.8125rem; - line-height: 1.5; + font-size: 1rem; + line-height: 1.6; margin-bottom: 0.875rem; white-space: normal; word-break: normal; @@ -478,8 +440,8 @@ } .tagBadge { - padding: 0.25rem 0.5625rem; - font-size: 0.6875rem; + padding: 0.375rem 0.625rem; + font-size: 0.75rem; } .scrollbarWrapper { @@ -488,10 +450,8 @@ margin-top: 1.5rem; } - .scrollbar { + .progressTrack { width: min(12.5rem, 60vw); - height: 1rem; - --thumb-width: 3.75rem; } } diff --git a/src/components/ProjectsSection.tsx b/src/components/ProjectsSection.tsx index 107708d..3496b8d 100644 --- a/src/components/ProjectsSection.tsx +++ b/src/components/ProjectsSection.tsx @@ -39,8 +39,12 @@ export const ProjectsSection: React.FC = () => { useEffect(() => { syncScrollState(); - window.addEventListener('resize', syncScrollState); - return () => window.removeEventListener('resize', syncScrollState); + const viewport = viewportRef.current; + if (!viewport) return; + + const resizeObserver = new ResizeObserver(syncScrollState); + resizeObserver.observe(viewport); + return () => resizeObserver.disconnect(); }, []); const scrollByCard = (direction: -1 | 1) => { @@ -182,14 +186,6 @@ export const ProjectsSection: React.FC = () => { dragState.current.moved = false; }; - const handleScrollbarChange = (event: React.ChangeEvent) => { - const viewport = viewportRef.current; - if (!viewport) return; - - const maxScroll = Math.max(0, viewport.scrollWidth - viewport.clientWidth); - viewport.scrollLeft = (Number(event.target.value) / 100) * maxScroll; - }; - const handleKeyDown = (event: React.KeyboardEvent) => { if (event.key === 'ArrowLeft') { event.preventDefault(); @@ -253,67 +249,71 @@ export const ProjectsSection: React.FC = () => { onKeyDown={handleKeyDown} tabIndex={0} aria-label="Scrollable project cards" + aria-roledescription="carousel" >
{projects.map((project) => ( -
-
- {project.title} -
- -
-
-

{project.title}

-
+ ))}
- + aria-valuemin={0} + aria-valuemax={100} + aria-valuenow={Math.round(scrollProgress)} + > + +