feat: 상단 네비게이션 바 / 하단 탭바 공통 컴포넌트 구현 - #28
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| className, | ||
| }: TopNavBarProps) { | ||
| return ( | ||
| <div className={cn(TOP_NAV_BAR_BASE, className)}> |
There was a problem hiding this comment.
header 태그를 사용해볼 수 있을 것 같네요!
|
|
||
| export const CENTER_SLOT = "flex min-w-0 flex-1 items-center justify-center"; | ||
|
|
||
| export const TITLE = "typo-headline-emphasized truncate text-gray-10"; |
There was a problem hiding this comment.
TopNavBar를 붙여보니 화면마다 타이틀 텍스트 스타일(폰트 크기 등)이 조금씩 다르게 쓰이는 경우가 있더라구요. title: string prop으로 스타일(typo-headline-emphasized)을 고정해두기보다, children만 받아서 호출하는 쪽에서 원하는 타이포로 직접 렌더링하게 하면 더 유연할 것 같습니다!
| @@ -0,0 +1,20 @@ | |||
| export const TOP_NAV_BAR_BASE = "flex h-[5.6rem] w-full items-center gap-[1rem] px-6"; | |||
|
|
|||
| export const BACK_BUTTON_WRAPPER = | |||
There was a problem hiding this comment.
BackButton도 스타일이 고정되어 있는데, 실제로는 화면마다 조금씩 다른 CSS가 적용되는 경우가 있더라구요. 디자인팀에 확인해서 스타일을 하나로 통일하거나, 그게 어렵다면 className을 열어서 화면별로 커스텀할 수 있게 해주면 좋을 것 같습니다!
There was a problem hiding this comment.
혹시 어디 부분이 다른지 알 수 있을까요?! 제가 hifi 확인했을 때는 다 같은 스타일같아서 여쭤봅니다!
| @@ -0,0 +1,20 @@ | |||
| export const TOP_NAV_BAR_BASE = "flex h-[5.6rem] w-full items-center gap-[1rem] px-6"; | |||
There was a problem hiding this comment.
h-[5.6rem], gap-[1rem]처럼 rem을 직접 쓰는 arbitrary value와 px-6처럼 테일윈드 기본 스페이싱 단위가 한 파일 안에 섞여 있네요! 둘 중 하나로 통일하면 좋을 것 같습니다!
There was a problem hiding this comment.
고민하다가 쓸수있는건 토큰으로 썼었는데,, 통일하는게 맞을 것 같네요 수정했습니다!!
|
|
||
| export const BACK_BUTTON_WRAPPER = | ||
| "relative size-[4.4rem] shrink-0 rounded-full shadow-[0px_0.8rem_4rem_0px_color-mix(in_srgb,var(--color-black)_10%,transparent)]"; | ||
| export const BACK_BUTTON_RING_CLIP = |
There was a problem hiding this comment.
BackButton 안에 absolute로 겹쳐놓은 레이어가 5개나 되네요(링, 링 반짝임, 채우기 테두리, 흰색 채우기, 대각선 그라데이션, 버튼 본체).
그리고 그 중 대각선 그라데이션 하나는 background 값이 너무 길고 복잡해서 코드만 보면 뭘 하는 스타일인지 바로 이해하기 어려운 것 같아요. 레이어를 좀 줄이거나 긴 그라데이션 값만이라도 변수로 빼서 이름을 붙여주면 나중에 읽기 훨씬 편할 것 같습니다!
There was a problem hiding this comment.
변수로 빼려고 했었는데 조금 애매해지는것 같아 최대한 레이어를 줄이고 하나로 합쳤고, 주석을 추가했습니다! (7be269a)
| import TabItem from "./TabItem"; | ||
| import { BOTTOM_TAB_BAR_BASE } from "./theme"; | ||
|
|
||
| const TABS = [ |
There was a problem hiding this comment.
현재 develop에 머지되어있는 route 타입에서 필요한 타입만 pick 해서 사용해볼 수 있을 것 같습니다~
|
|
||
| export default function BottomTabBar({ activeTab, onTabChange, className }: BottomTabBarProps) { | ||
| return ( | ||
| <div className={cn(BOTTOM_TAB_BAR_BASE, className)}> |
| @@ -0,0 +1,8 @@ | |||
| export const BOTTOM_TAB_BAR_BASE = | |||
There was a problem hiding this comment.
BottomTabBar 스타일을 theme.ts로 따로 빼신 이유가 따로 있을까요?? 지금 보니 클래스가 몇 줄 안 되고, TopNavBar처럼 레이어가 복잡한 것도 아니라서 굳이 파일을 분리하지 않고 컴포넌트 안에 인라인으로 둬도 충분할 것 같아요!
그리고 shadow-[0px_0px_4rem_0px_color-mix(...)]처럼 매번 arbitrary value로 shadow를 새로 정의하시는데, 비슷한 shadow가 다른 컴포넌트에서도 반복된다면 토큰이나 공용 유틸로 빼는 것은 어떻게 생각하시나요??
There was a problem hiding this comment.
좋습니다! 추후에도 쓰일 것 같아서 디자인 토큰으로 분리했습니다.
그리고 BottomTabBar에서 쓰이는 theme.ts도 인라인으로 붙이고 삭제했습니다! (7be269a)
| onBack: () => void; | ||
| className?: string; | ||
| } & ( | ||
| | { rightIcon?: undefined; rightIconLabel?: undefined; onRightIconClick?: undefined } |
There was a problem hiding this comment.
베이스 prop 과 분리해서 RightIconSlot 타입으로 별도 선언하면 가독성이 좋아질 것 같아용
| return ( | ||
| <div className={cn(TOP_NAV_BAR_BASE, className)}> | ||
| <BackButton onBack={onBack} /> | ||
| <div className={CENTER_SLOT}>{children ?? <span className={TITLE}>{title}</span>}</div> |
There was a problem hiding this comment.
제안) title/children 둘 다 옵셔널이라 사용처에서 동시에 넘기면 타입 에러 없이 title이 무시돼요. rightIcon 트리플처럼 discriminated union으로 묶어서 둘 중 하나만 넘기게 강제하는 방법 적용해도 좋을 것 같아요. 다른 방안 있으면 그것도 편하게 제안해주세요!
There was a problem hiding this comment.
동희님이 title이 여러가지 타이포를 쓴다고 알려주셔서 children으로만 받도록 수정했습니다! RightIconSlot도 분리하였습니당 (8366f25)
BottomTabBar에서 쓸 홈/유저 아이콘(채움·윤곽선)을 Figma에서 다운로드해 shared/assets/icons/mono에 추가하고 codegen으로 shared/ui/icons에 반영.
shared/ui 컴포넌트로 뒤로가기 버튼, 타이틀(또는 커스텀 children), 우측 아이콘(prop 슬롯)을 지원. 뒤로가기 버튼은 Figma의 glass 이펙트를 근사한 반투명 배경(배경색에 따라 톤이 달라짐)과 좌상단/우하단 하이라이트 링을 별도 컴포넌트(BackButton)로 분리해 구현. rightIcon 유무와 관계없이 오른쪽 슬롯 너비를 항상 예약해 타이틀 중심이 흔들리지 않도록 함.
shared/ui 컴포넌트로 홈/유저 탭, 활성 탭에 따라 배경 pill과 아이콘 (채움/윤곽선)이 토글되는 하단 탭바 구현. 탭 목록은 TABS 배열로 데이터화해 중복 없이 관리하고, 배열에서 파생한 Tab 타입을 index.ts로 공개. 컨테이너 너비는 고정값 대신 내용에 맞춰 늘어나도록 해서 탭 개수 변경에 대응 가능. 그림자 색상은 top-nav-bar와 동일하게 color-mix()로 토큰 참조.
리뷰 반영: 루트 div를 각각 header, nav로 교체
리뷰 반영: - title/children 동시 전달 시 title이 무시되는 문제 해결 (children만 사용) - 화면별로 다른 타이틀 타이포를 호출부가 직접 렌더링하도록 변경 - rightIcon 관련 타입을 RightIconSlot으로 분리
- 반복되는 그림자 값을 styles/tokens/shadow.css의 --shadow-* 토큰으로 추출 (Figma에 이름 붙은 스타일은 아니라 토큰 대응표에 값 그대로 기재) - BottomTabBar theme.ts 제거, 클래스를 컴포넌트에 인라인으로 통합 - BackButton: px-6(Tailwind 기본 토큰)과 arbitrary rem 혼용 정리, overflow-hidden이 자기 자신의 box-shadow는 클리핑하지 않는 점을 활용해 RING_CLIP/FILL_WHITE를 WRAPPER에, FILL_CLIP/FILL_DIAGONAL을 FILL로 병합 (DOM 레이어 7개 -> 4개), 각 상수 역할 설명 주석 추가
리뷰 반영: TABS의 id를 route 타입(ROUTES)에서 필요한 키만 Pick해서 검증하도록 변경. "user" id를 route 키에 맞춰 "my"로 수정.
Summary
shared/ui에 상단 네비게이션 바(TopNavBar)와 하단 탭바(BottomTabBar)를 독립 컴포넌트로 구현했습니다. 뒤로가기 버튼은 Figma의 glass 이펙트를 근사한 반투명 배경으로, 하단 탭바는 홈/유저 2개 탭에 활성 상태에 따라 아이콘(채움/윤곽선)과 pill 배경이 토글되도록 만들었습니다.Related Issues
PR Point (To Reviewer)
status(Default/navigation) variant와 5-dot 진행 인디케이터는 이번 구현 범위에서 제외했습니다 (후속 이슈에서 필요 시 별도 작업).GLASS네이티브 이펙트(굴절/스페큘러 하이라이트)라 CSS로 정확히 재현이 불가능해서,mix-blend-mode기반 반투명 레이어로 근사했습니다 — 배경색에 따라 톤이 자연스럽게 반응합니다.BottomTabBar는 지금은 홈/유저 2개로 고정이지만, 탭 목록을TABS배열로 데이터화하고Tab타입을 거기서 파생시켜서 나중에 탭이 늘어나도 로직 쪽은 큰 변경 없이 확장 가능합니다. 컨테이너 너비도 고정값이 아니라 내용에 맞춰 늘어나도록 했습니다.onBack,onTabChange콜백으로 위임) — 실제 페이지 이동은 후속 라우팅 이슈에서 연결하면 됩니다.Screenshot
ETC
없음