배경
Radio 컴포넌트의 두 variant(primary/outline)가 실제로는 서로 다른 두 가지 용도로 쓰이고 있었다: primary는 Select(난이도 선택 등) 안에서만, outline은 이미지 위 위치 표시자로만 쓰인다. 이제 Select(shadcn dropdown-menu 스타일 컴파운드 패턴, Figma "Select part" 참고)를 새로 만들면서, primary는 Select 내부 구현으로 옮기고 Radio에는 outline 스타일만 남겨 각 컴포넌트가 실제 역할과 이름이 일치하도록 정리한다.
작업 내용
완료 조건
Radio는 variant prop 없이 단일 스타일(구 outline)만 렌더링하고, 여전히 클릭 가능한 <button>이다
<Select.Root value={value} onValueChange={setValue}><Select.Trigger /><Select.Content><Select.Item value="난이도 하">난이도 하</Select.Item>...</Select.Content></Select.Root> 형태로 조합 가능하다
Select.Trigger를 클릭하면 Select.Content가 열리고, 바깥을 클릭하거나 Esc를 누르면 닫힌다
Select.Item을 클릭하면 onValueChange가 호출되고 Select.Content가 자동으로 닫히며, Select.Trigger에 선택된 라벨과 인디케이터 색이 반영된다
Select.Item은 Radio(또는 <button>)를 중첩하지 않는다 — 버튼 안에 버튼이 없다
tsc -b 타입체크 통과
참고 자료
- shadcn dropdown-menu 컴파운드 패턴: https://ui.shadcn.com/docs/components/base/dropdown-menu
- Figma "Select part" 프레임: 닫힌 상태(흰 pill + 회색 dot) / 열린 상태(흰 pill + 노란 dot, 아래로 "난이도 하/중/상" 3개 옵션 리스트) — grill-with-docs 세션에서 공유
- Figma Radio 상세:
primary(둥근 사각형, Select 전용) vs outline(원형, 이미지 위 위치 표시자) 두 스타일 비교 스크린샷 — grill-with-docs 세션에서 공유
배경
Radio 컴포넌트의 두 variant(
primary/outline)가 실제로는 서로 다른 두 가지 용도로 쓰이고 있었다:primary는 Select(난이도 선택 등) 안에서만,outline은 이미지 위 위치 표시자로만 쓰인다. 이제Select(shadcn dropdown-menu 스타일 컴파운드 패턴, Figma "Select part" 참고)를 새로 만들면서,primary는Select내부 구현으로 옮기고Radio에는outline스타일만 남겨 각 컴포넌트가 실제 역할과 이름이 일치하도록 정리한다.작업 내용
src/shared/ui/radio/Radio.tsx에서variantprop 제거,outline스타일(원형, 반투명 배경 + blur)만 남김. 기존<button>동작(클릭 가능)은 그대로 유지src/shared/ui/select/신규:Select.Root/Select.Trigger/Select.Content/Select.Item컴파운드 컴포넌트 구현Select.Root:value/onValueChange,open/onOpenChange둘 다 제어 가능(안 넘기면 내부 상태로 폴백).<div className="relative">를 렌더링해Trigger/Content의 포지셔닝 컨테이너 역할까지 겸함. React Context로 하위 컴포넌트에 상태 전달Select.Trigger: 네이티브<button>. 현재 선택된Item의 라벨과 인디케이터 색을 자동으로 그림(별도 조합 불필요)Select.Content:absolute top-full left-0로Trigger바로 아래 위치(floating-ui 등 라이브러리 도입 안 함). 바깥 클릭 또는 Esc 키로 닫힘Select.Item: 클릭 가능한 요소(버튼 아님,Radio를 직접 중첩하지 않음). 클릭 시onValueChange호출 + 자동으로 닫힘. 기존Radio의primaryvariant였던 인디케이터 모양(둥근 사각형, 회색→노랑 배경 + 회색→검정 dot)을 내부 구현으로 그림role/aria-*등 기본적인 시맨틱만 부여. 방향키 옵션 네비게이션은 범위 밖(네이티브 버튼의 Enter/Space 기본 동작만 활용)src/shared/ui/select/index.ts로 public API 노출src/shared/ui/radio/index.ts등 기존 export 정리(제거된variantprop 관련 타입 export 정리)완료 조건
Radio는variantprop 없이 단일 스타일(구outline)만 렌더링하고, 여전히 클릭 가능한<button>이다<Select.Root value={value} onValueChange={setValue}><Select.Trigger /><Select.Content><Select.Item value="난이도 하">난이도 하</Select.Item>...</Select.Content></Select.Root>형태로 조합 가능하다Select.Trigger를 클릭하면Select.Content가 열리고, 바깥을 클릭하거나 Esc를 누르면 닫힌다Select.Item을 클릭하면onValueChange가 호출되고Select.Content가 자동으로 닫히며,Select.Trigger에 선택된 라벨과 인디케이터 색이 반영된다Select.Item은Radio(또는<button>)를 중첩하지 않는다 — 버튼 안에 버튼이 없다tsc -b타입체크 통과참고 자료
primary(둥근 사각형, Select 전용) vsoutline(원형, 이미지 위 위치 표시자) 두 스타일 비교 스크린샷 — grill-with-docs 세션에서 공유