From 976c2cab21baa20eefafb4ba228fa4a8e418e822 Mon Sep 17 00:00:00 2001 From: yoonyoungyang Date: Thu, 12 Feb 2026 22:05:56 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B0=B0=EB=84=88=20=EB=A7=88=EC=A7=84?= =?UTF-8?q?=EA=B0=92=20=EC=88=98=EC=A0=95=20&=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=ED=83=AD=20=EC=9D=B8=EB=94=94=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=9C=84=EC=B9=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/routes/home/components/BannerCarousel.tsx | 8 ++- app/routes/home/components/CategoryTabs.tsx | 69 +++++++++++++++---- 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/app/routes/home/components/BannerCarousel.tsx b/app/routes/home/components/BannerCarousel.tsx index 416328b..68dd739 100644 --- a/app/routes/home/components/BannerCarousel.tsx +++ b/app/routes/home/components/BannerCarousel.tsx @@ -26,7 +26,11 @@ const fashionBanners: BannerItem[] = [ { src: bannerFashion3, alt: "패션 배너 3" }, ]; -export default function BannerCarousel({ category }: { category: CategoryKey }) { +export default function BannerCarousel({ + category, +}: { + category: CategoryKey; +}) { const banners = category === "beauty" ? beautyBanners : fashionBanners; const [current, setCurrent] = useState(0); @@ -51,7 +55,7 @@ export default function BannerCarousel({ category }: { category: CategoryKey }) }, [start, stop]); return ( -
+
void; }) { - const tabs: { key: CategoryKey; label: string }[] = [ - { key: "beauty", label: "뷰티" }, - { key: "fashion", label: "패션" }, - ]; + const tabs = useMemo( + () => + [ + { key: "beauty" as const, label: "뷰티" }, + { key: "fashion" as const, label: "패션" }, + ] satisfies { key: CategoryKey; label: string }[], + [], + ); + + const wrapRef = useRef(null); + const [wrapWidth, setWrapWidth] = useState(0); + + useLayoutEffect(() => { + const el = wrapRef.current; + if (!el) return; + + const measure = () => setWrapWidth(el.clientWidth); + measure(); + + const ro = new ResizeObserver(() => measure()); + ro.observe(el); + + return () => ro.disconnect(); + }, []); + + const activeIndex = Math.max( + 0, + tabs.findIndex((t) => t.key === value), + ); + + const pxToRem = (px: number) => { + if (typeof window === "undefined") return `${px / 16}rem`; + const root = window.getComputedStyle(document.documentElement).fontSize; + const base = Number.parseFloat(root) || 16; + return `${px / base}rem`; + }; + + const trackWidth = Math.max(0, wrapWidth - SIDE_MARGIN_PX * 2); + const indicatorWidth = trackWidth / tabs.length; + const indicatorX = SIDE_MARGIN_PX + indicatorWidth * activeIndex; return ( -
+
{tabs.map((t) => { const active = t.key === value; @@ -37,15 +78,13 @@ export default function CategoryTabs({
-
-
-
+
); }