Skip to content

feat: Radio 재정비 + Select 컴파운드 컴포넌트 추가 - #20

Open
hamxxn wants to merge 3 commits into
developfrom
feat/issue-19
Open

feat: Radio 재정비 + Select 컴파운드 컴포넌트 추가#20
hamxxn wants to merge 3 commits into
developfrom
feat/issue-19

Conversation

@hamxxn

@hamxxn hamxxn commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Radio의 두 variant(primary/outline)가 서로 다른 용도로 쓰이고 있던 걸 정리했다. primary(둥근 사각형 + dot)는 Select 전용 인디케이터로 옮기고, Radio에는 실제로 계속 쓰이는 outline 스타일(원형, 반투명+blur)만 남겼다. shadcn dropdown-menu 스타일의 Select 컴파운드 컴포넌트(Root/Trigger/Content/Item)를 새로 추가했다.

사용 방법

import { Select } from "@/shared/ui/select";
import { Radio } from "@/shared/ui/radio";

// Select — Trigger 라벨은 고정 텍스트, 선택 여부는 dot 색으로만 표시
const [value, setValue] = useState<string | undefined>(undefined);

<Select.Root value={value} onValueChange={setValue}>
  <Select.Trigger>난이도</Select.Trigger>
  <Select.Content>
    <Select.Item value="난이도 하">난이도 하</Select.Item>
    <Select.Item value="난이도 중">난이도 중</Select.Item>
    <Select.Item value="난이도 상">난이도 상</Select.Item>
  </Select.Content>
</Select.Root>

// value/open을 안 넘기면 비제어 모드로 내부 상태 사용
<Select.Root defaultValue="난이도 하" onOpenChange={console.log}>
  ...
</Select.Root>

// Radio — variant 없이 단일(구 outline) 스타일만, 여전히 클릭 가능한 버튼
<Radio isSelected={isMarked} onClick={() => setIsMarked((v) => !v)} />

Related Issues

PR Point (To Reviewer)

  • Trigger 라벨은 고정 텍스트다. 처음엔 이슈 스펙대로 "선택된 Item의 라벨을 Trigger가 자동으로 그린다"로 구현했는데, 실제 Figma 레퍼런스를 다시 보니 Trigger 텍스트(예: "난이도")는 선택 여부와 무관하게 고정이고 dot 색(회색↔노랑)만 선택 상태를 나타내는 구조였다. 그래서 라벨 자동 조회를 위해 만들었던 Context 등록 로직(registerItemLabel 등)을 전부 걷어내고 Select.Triggerchildren을 그대로 받는 방식으로 단순화했다. 이슈 본문은 옛 스펙(자동 라벨) 그대로라 갱신이 필요하다.
  • Content가 Trigger 뒤로 겹친다. top-full로 Trigger 바로 아래 붙이면 Trigger(rounded-[1.6rem])와 Content(rounded-[2rem])의 border-radius가 달라서 이음새가 어긋나 보였다. Trigger에 더 높은 z-index를 줘서 겹치게 만들어 이음새를 없앤 아이디어 자체는 유지하되, 처음엔 Content를 absolute top-0 + pt-[6rem]로 구현했었다. 그런데 이렇게 하면 Content가 레이아웃 흐름에서 완전히 빠져서, Select를 여러 개 세로로 쌓아둔 상태(지금 HomePage처럼)에서 하나를 열면 자기 높이만큼 아래 형제 Select를 밀어내지 못하고 그 위를 그냥 덮어버리는 문제가 있었다. absolute 대신 relative -mt-[6rem]로 바꿔서, 시각적으로는 여전히 Trigger 뒤로 파고들지만 레이아웃 공간은 정상적으로 차지해 열렸을 때 아래 요소가 밀려나도록 고쳤다. 바깥 클릭 감지는 Trigger+Content를 감싸는 wrapper 전체 기준이라 겹쳐도 오작동하지 않는다.
  • Trigger/Content 폭은 Trigger 쪽이 기준이다. 처음엔 Select.Rootinline-block으로, Select.Contentinset-x-0로 Trigger 폭을 상속받게 했는데, Content가 absolute를 벗어나 일반 흐름으로 옮기면서 inset-x-0 트릭을 쓸 수 없게 됐다. 대신 Select.Rootinline-flex flex-col로 바꿔서, flex가 자식 중 가장 넓은 요소(Trigger) 기준으로 컨테이너 폭을 잡고 align-items: stretch로 Content도 같은 폭으로 늘어나게 했다.
  • Select.Item<button>이 아니라 클릭 가능한 <li role="option">이라 Radio(버튼)를 중첩하지 않는다. 방향키 옵션 네비게이션은 범위 밖(네이티브 버튼의 Enter/Space만 활용).
  • floating-ui 등 포지셔닝 라이브러리는 도입하지 않았다 — 뷰포트 경계 충돌 대응이 필요해지면 그때 추가.

Screenshot

스크린샷 2026-08-09 오전 11 31 49 스크린샷 2026-08-09 오전 11 31 57

ETC

없음

hamxxn added 2 commits August 9, 2026 11:34
primary variant는 Select 전용 인디케이터로 옮겨갔으므로, Radio에는
실제로 남아 쓰이는 outline 스타일(원형, 반투명+blur)만 남긴다.
클릭 가능한 <button> 동작은 그대로 유지.
shadcn dropdown-menu 스타일의 Root/Trigger/Content/Item 컴파운드
패턴으로 구현. Context로 열림/선택값 상태를 공유하고, floating-ui
없이 CSS 포지셔닝(Trigger 뒤로 Content가 겹치는 구조)으로 배치한다.
바깥 클릭·Esc로 닫히고, 옵션 선택 시 자동으로 닫힌다.

Trigger의 라벨은 선택값과 무관한 고정 텍스트이고, 선택 여부는
dot 색으로만 표시한다.
@hamxxn hamxxn self-assigned this Aug 9, 2026
@hamxxn hamxxn linked an issue Aug 9, 2026 that may be closed by this pull request
4 tasks
@vercel

vercel Bot commented Aug 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
aligner Ready Ready Preview Aug 11, 2026 12:22am

Comment thread src/shared/ui/select/theme.ts Outdated
Comment thread src/shared/ui/select/theme.ts Outdated

// Select.Item 스타일
export const SELECT_ITEM = {
base: "flex items-center justify-between pl-[1.6rem] pr-[2.6rem] py-[1.9rem] text-tertiary-100 active:text-primary-500 typo-body-regular",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cursor-pointer 추가하는 것 어떤가요?

그리고 지금은 active:text-primary-500가 있어서 클릭하는 순간에만 텍스트 색깔이 달라지고 selected 되었을 때 바뀌는 색깔은 점(동그라미)밖에 없어서욮!! 텍스트도 함께 변경해주시면 감사하겠습니다!

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

텍스트 자체는 변경이 안되는 것으로 알고있어요!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Select part

요부분 말씀하시는걸까요?

Content를 absolute 대신 relative + 음수 margin-top으로 Trigger 뒤에
겹치게 해서, 열렸을 때 레이아웃 공간을 정상적으로 차지하고 아래
형제 요소를 밀어내도록 했다. Root wrapper도 inline-flex flex-col로
바꿔 Trigger/Content 너비가 자연스럽게 맞도록 했다.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@thwjddlqslek thwjddlqslek left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

수고하셨습니다!


const setValue = useCallback(
(next: string) => {
setUncontrolledValue(next);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

controlled 일때도 안쓰이는 내부 상태가 계속 갱신되는 상태인데, 분기 추가해서 controlled일땐 건드리지 않게 해두면 좋을 것 같아요!

+open 도 동일합니닷

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Radio 재정비 + Select 컴파운드 컴포넌트 추가

3 participants