Resizable
화면 안의 크기를 손으로 바꾸는 손잡이 — HR 관리 화면의 목록 | 상세 나누기(목록 폭)와 HR 대시보드 편집 모드의 위젯 크기. 768 이상의 데스크톱에서만 쓴다. 폰은 나누지 않는다 — 목록 → 상세 · 탭이고, 옆 패널은 Side Panel(1280 미만은 Bottom Sheet)이다.
SEED 에는 Resizable 이 없다 — 모바일 앱 중심이라 끌어서 나누는 패널이 없다. 그래서 값은 모두 groo 가 정했다(2026-10-10 사용자 결정 12B · 13A) — 나누기는 남기고 고친다(보이는 선 1px + 가운데 손잡이 표시 · 누르는 폭 16 · 이름 "목록 너비" · ← → 5% · Home · End · 목록 20 ~ 40% · 화면마다 폭 기억 · 한 번 누르면 25 ↔ 40), 위젯 크기도 남기고 고친다(모서리 손잡이 대비 3 이상 · 토큰 색 자리표시 · ⋮ "크기"). 키보드는 WAI-ARIA APG Window Splitter 다. 옛 Resizable 스펙(가로 · 세로 · 손잡이 상자 12 × 16 · 누르는 폭 4 · hover 변화 없음)과 DESIGN.md v71 의 반대되던 글(손잡이 4 ~ 6px · hover 브랜드 색 · ±1% · "localStorage 권장")을 대신한다.
수치 원본은 resizable.yaml이다. 수치 표 자리([표: …])와 그림 자리([그림: …])는 사이트가 그 파일로 그린다 — GitHub 에서는 그 파일 · 그림 코드로 가는 링크로 보인다.
직접 골라 보기
손잡이를 끌거나, 한 번 눌러 25 ↔ 40 으로 바꾸거나, 초점을 두고 ← → · Home · End · Enter 를 눌러 보라 — 아래 글이 왼쪽 · 오른쪽 폭과 읽기를 따라간다. "화면 다시 열기" 는 기억한 폭으로 연다. 위젯은 모서리를 끌거나 ⋮ → 크기를 골라 보라.
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable"
{/* 768 이상만 — 목록 20 ~ 40%(오른쪽은 60% 아래로 줄지 않는다), 화면마다 폭을 기억 */}
<ResizablePanelGroup storageKey="hr-admin-company">
<ResizablePanel id="company-tree" defaultSize={25} minSize={20} maxSize={40}>
{departmentTree}
</ResizablePanel>
<ResizableHandle label="목록 너비" toggleSize={40} />
<ResizablePanel>{orgChart}</ResizablePanel>
</ResizablePanelGroup>
{/* 지금 목록 25% — — */}Anatomy
| 부위 | 설명 |
|---|---|
| ⓐ Panel | 목록 패널 — 왼쪽, 폭이 바뀐다(20 ~ 40%). 상세 패널은 남는 폭이다. |
| ⓑ Line | 선 — 1px stroke-neutral-weak. 올리거나 초점 · 끄는 동안 짙어진다. |
| ⓒ Grip | 손잡이 표시 — 선 가운데 4 × 24 stroke-neutral-solid. |
| ⓓ Handle | 손잡이 — 선 가운데로 누르는 폭 16. 이름 "목록 너비". |
| ⓔ Widget Handle | 위젯 모서리 손잡이 — 오른쪽 아래 24 × 24, 표식 9 × 9. |
| ⓕ Placeholder | 끄는 동안 자리표시 — 위젯이 놓일 칸. |
| ⓖ Size Menu | 위젯 머리 ⋮ 와 그 "크기" 메뉴 — 끌지 않는 대안. |
부위
| 부위 | 설명 |
|---|---|
group | 나눈 자리 — split 은 목록 | 상세 두 패널, widget 은 대시보드 칸(격자) |
panel | 목록 패널(왼쪽) — 폭이 바뀐다. 상세 패널은 남는 폭 |
handle | 손잡이 — 두 패널 사이. role="separator", 누르는 폭 16 |
line | 보이는 선 — 1px |
grip | 가운데 손잡이 표시 — 4 × 24 |
widgetHandle | 위젯 모서리 손잡이 — 오른쪽 아래, 누르는 24 × 24 |
widgetMark | 모서리 표식 — 9 × 9 꺾쇠 |
placeholder | 끄는 동안 자리표시 — 위젯이 놓일 칸 |
ghost | 끄는 동안 포인터를 따라오는 위젯 |
moreButton | 위젯 머리 ⋮ — "크기" 메뉴를 연다(Button ghost · iconOnly · medium) |
sizeMenu | 위젯 ⋮ 의 "크기" — 끌지 않는 대안(Menu) |
focusRing | 키보드 포커스 링(웹) — 손잡이 둘레 |
Properties
패널 나누기(split)
HR 관리 > 역할 · 사용자 · 회사 · 부서 사용자는 목록 | 상세로 나뉘고, 그 사이 손잡이로 목록 폭을 바꾼다. 목록은 기본 25%(1280 에서 본문 992 의 248)이고 20 ~ 40% 사이에서만 바뀐다 — 오른쪽 상세는 60% 아래로 줄지 않는다. 지금 회사 · 부서 사용자는 오른쪽 최소가 없어 End 한 번에 조직도 · 사용자 칸이 0폭이 된다. 나누기를 걷고 25 / 75 로 고정하는 안(12A)은 고르지 않았다 — 긴 부서 이름 · 깊은 나무를 볼 때 목록을 넓힌다.
나눈 자리 · 목록 패널
| kind | group 간격 | group 칸 수 | 나누는 방향 | group 바뀌는 폭 | group 격자 한 줄 | panel 너비 | panel 최소 너비 | panel 최대 너비 | panel 누르면 넓게 | panel 폭 기억 | 설명 |
|---|---|---|---|---|---|---|---|---|---|---|---|
split (기본) | — | — | 가로 — 목록 | 상세 — 세로 나누기 · 셋 이상 나누기는 쓰는 곳이 없어 두지 않는다 | 768px 이상 — 768 미만은 나누지 않는다 — 역할 · 사용자는 목록 → 상세, 회사는 탭(지금과 같다) | — | 25% — 목록 패널의 기본 폭 — 1280 에서 본문 992 의 248(사이드바 240 · 여백 24 × 2) | 20% — 목록이 이보다 좁아지지 않는다 | 40% — 오른쪽 상세가 60% 아래로 줄지 않는다 — 지금 회사 · 부서 사용자는 0폭까지 민다 | 40% — 손잡이를 한 번 누르면(Enter) 기본 25 ↔ 넓게 40 | 화면마다 기억 — 끌거나 누른 폭을 브라우저에 저장한다(localStorage — 키는 화면: 역할 · 사용자 · 회사 · 부서 사용자). 사이드바 접힘처럼 다시 열면 그 폭 | 패널 나누기 — 목록 | 상세(기본) |
widget | spacing-x4 16px — 칸 사이 16 — 지금 HR 값 | 12 — 대시보드 격자 — 지금 HR 값(react-grid-layout). 크기 · 위치는 편집 모드에서만 바꾸고 저장 단추로 서버에 저장한다 | — | — | 72px — 격자 한 줄 — 지금 HR 값 | — | — | — | — | — | 대시보드 위젯 크기 — 편집 모드에서만 |
손잡이
선은 1px stroke-neutral-weak(흰 면 위 1.23 · 다크 1.56)로 경계만 긋고, 가운데의 손잡이 표시 4 × 24 stroke-neutral-solid(4.18 · 다크 4.13 — WCAG 1.4.11 의 3 이상)가 끌 수 있는 자리를 알린다. 마우스를 올리거나 키보드 초점 · 끄는 동안에는 선 전체가 손잡이 표시와 같은 색으로 짙어진다. 누르는 폭은 선 가운데로 양쪽 8 — 16 이다. 768 이상 데스크톱의 마우스 경계라 44 가 아니다(사용자 결정 12B — 지금 5). 손잡이를 끌지 않고 한 번 눌렀다 떼면(3px 안) 기본 25 ↔ 넓게 40 으로 바뀐다 — 끌기를 못 하는 사람의 대안(WCAG 2.5.7)이다.
손잡이
| kind | handle 너비 | handle 커서 | handle 터치 영역 | handle 누름 | handle 키보드 | handle 이름 | handle 터치 동작 | handle 끌기 시작 | handle 한 번에 | 설명 |
|---|---|---|---|---|---|---|---|---|---|---|
split (기본) | 16px — 누르는 폭 — 선 가운데로 양쪽 8. 768 이상 데스크톱의 마우스 경계라 44 가 아니다(사용자 결정 12B) | col-resize | 16 × 패널 높이 — AA ⚠(폭 16) · AAA ✗ — 같은 일을 누르기(25 ↔ 40) · 키보드로도 한다 | 누르면 25 ↔ 40 — 끌지 않고 한 번 눌렀다 떼면(3px 안) 기본 25 ↔ 넓게 40 — WCAG 2.5.7 Dragging Movements 의 대안 | ← → 5% · Home 20% · End 40% · Enter 25 ↔ 40 — APG Window Splitter — Shift + 화살표는 두지 않는다 | 목록 너비 — role=separator · aria-orientation=vertical · aria-valuenow(목록 %) · aria-valuemin 20 · aria-valuemax 40 · aria-controls(목록 패널) | none — 끄는 동안 화면이 스크롤되지 않게 | 3px — 누른 채 3px 넘게 움직이면 끌기 — 그 안에서 놓으면 누르기 | 5% — ← → 한 번에 — 지금 HR 은 10% | 패널 나누기 — 목록 | 상세(기본) |
widget | — | — | — | — | — | — | — | — | — | 대시보드 위젯 크기 — 편집 모드에서만 |
선 · 손잡이 표시
| kind | line 너비 | line 배경 | line 전환 대상 | line 전환 시간 | line 전환 곡선 | grip 높이 | grip 너비 | grip 모서리 | grip 배경 | grip 자리 | 설명 |
|---|---|---|---|---|---|---|---|---|---|---|---|
split (기본) | 1px | color-stroke-neutral-weak #E5E8EF · 다크 #404757 — 흰 면 위 1.23 · 다크 1.56 — 경계일 뿐이고 손잡이는 가운데 표시가 알린다 | background-color | motion-duration-color-transition 150ms | motion-ease-easing cubic-bezier(0.35, 0, 0.35, 1) | 24px | 4px | radius-r0_5 2px | color-stroke-neutral-solid #767C8B · 다크 #838997 — 흰 면 위 4.18 · 다크 4.13(WCAG 1.4.11 ≥ 3) — 지금 HR 은 선 · 손잡이가 모두 #EEEEEE 1.11 | 선 가운데 — 패널 높이의 가운데 | 패널 나누기 — 목록 | 상세(기본) |
widget | — | — | — | — | — | — | — | — | — | — | 대시보드 위젯 크기 — 편집 모드에서만 |
위젯 크기(widget)
HR 대시보드 편집 모드에서 위젯의 크기를 바꾼다 — 크기 · 위치는 편집 모드에서만 바꾸고 저장 단추로 서버에 저장한다(지금과 같다). 오른쪽 아래 모서리 손잡이는 누르는 자리 24 × 24 · 표식 9 × 9 꺾쇠 2px stroke-neutral-solid(카드 면 위 4.18 · 다크 4.13)다. 끄는 동안에는 놓일 칸에 자리표시(옅은 브랜드 바탕 + 1px 브랜드 점선 · 모서리 16)를 보이고, 위젯은 그림자 shadow-s3 로 포인터를 따라온다. 끌지 못하는 사람은 위젯 ⋮ 의 "크기" 에서 고른다(아래). 크기를 위젯마다 고정하는 안(13B)은 고르지 않았다.
위젯 격자
| kind | group 간격 | group 칸 수 | 나누는 방향 | group 바뀌는 폭 | group 격자 한 줄 | 설명 |
|---|---|---|---|---|---|---|
split (기본) | — | — | 가로 — 목록 | 상세 — 세로 나누기 · 셋 이상 나누기는 쓰는 곳이 없어 두지 않는다 | 768px 이상 — 768 미만은 나누지 않는다 — 역할 · 사용자는 목록 → 상세, 회사는 탭(지금과 같다) | — | 패널 나누기 — 목록 | 상세(기본) |
widget | spacing-x4 16px — 칸 사이 16 — 지금 HR 값 | 12 — 대시보드 격자 — 지금 HR 값(react-grid-layout). 크기 · 위치는 편집 모드에서만 바꾸고 저장 단추로 서버에 저장한다 | — | — | 72px — 격자 한 줄 — 지금 HR 값 | 대시보드 위젯 크기 — 편집 모드에서만 |
모서리 손잡이 · 표식
| kind | widgetHandle 크기 | widgetHandle 커서 | widgetHandle 터치 영역 | widgetHandle 터치 동작 | widgetMark 크기 | widgetMark 테두리 | widgetMark 테두리 두께 | widgetMark 오른쪽 거리 | widgetMark 아래 거리 | 설명 |
|---|---|---|---|---|---|---|---|---|---|---|
split (기본) | — | — | — | — | — | — | — | — | — | 패널 나누기 — 목록 | 상세(기본) |
widget | 24px — 누르는 자리 24 × 24 — 오른쪽 아래 모서리. 지금 20 | se-resize | 24 × 24 — AA ✓ · AAA ✗ — 끌지 않는 대안은 ⋮ 의 "크기"(moreButton) | none | 9px — 오른쪽 · 아래 두 변만 그린 꺾쇠 — 지금 5 | color-stroke-neutral-solid #767C8B · 다크 #838997 — 카드 면 위 4.18 · 다크 4.13(≥ 3) — 지금 rgba(0,0,0,.4) 는 2.85 · 다크 1.14 | 2px | 6px — 위젯 오른쪽 끝에서 | 6px — 위젯 아래 끝에서 | 대시보드 위젯 크기 — 편집 모드에서만 |
자리표시 · 따라오는 위젯
| kind | placeholder 모서리 | placeholder 배경 | placeholder 테두리 | placeholder 테두리 두께 | placeholder 테두리 모양 | placeholder 보이기 | ghost 모서리 | ghost 그림자 | ghost 보이기 | 설명 |
|---|---|---|---|---|---|---|---|---|---|---|
split (기본) | — | — | — | — | — | — | — | — | — | 패널 나누기 — 목록 | 상세(기본) |
widget | radius-r4 16px — 위젯(Card 면)과 같은 16 | color-bg-brand-weak HR #E8F3EC · Desk #E8F1FE — 놓일 칸 — 브랜드 옅은 바탕. 지금은 라이브러리 기본 빨강 20% | color-stroke-brand-solid HR #075B3C · Desk #0147AD — 바닥 위 HR 7.56 · 다크 6.88 · 옅은 바탕 위 7.18 · 5.47(≥ 3) — 자리표시의 경계 | 1px — 비교 페이지 13A 그림은 1.5 였지만 크로미움은 1.5px 테두리를 1px 로 그린다(1 · 2배 화면 모두 — 레시피 하네스 실측) — 사용자가 본 모습 그대로 1 | dashed | 숨김 — 끄는 동안만 보인다 | radius-r4 16px | shadow-s3 — 떠 있는 동안만 | 숨김 — 끄는 동안 포인터를 따라오는 위젯 — 위젯 모양 그대로 | 대시보드 위젯 크기 — 편집 모드에서만 |
⋮ · 크기 메뉴
| kind | moreButton 크기 | moreButton 아이콘 크기 | moreButton 터치 영역 | moreButton 이름 | sizeMenu 항목 | sizeMenu 글 | 설명 |
|---|---|---|---|---|---|---|---|
split (기본) | — | — | — | — | — | — | 패널 나누기 — 목록 | 상세(기본) |
widget | 40px — Button ghost · iconOnly · medium — 표 · 줄의 ⋮ 와 같다. 위젯 머리 오른쪽 | 18px — lucide ellipsis-vertical | 44 × 44 — 보이는 40 바깥으로 2 씩 — 모서리 손잡이(24)의 끌지 않는 대안. AA ✓ · AAA ✓ | {위젯} 더보기 — 편집 모드에서만 — 위젯 제목을 붙인다 | 작게 — 4칸 · 보통 — 6칸 · 넓게 — 8칸 · 가득 — 12칸 — 누르면 바로 그 폭 — 높이는 그대로. 메뉴는 실행만이라 체크 · 라디오가 없고, 지금 크기 줄에 설명 "지금 크기"(menu.md) | 크기 — 메뉴 묶음 이름(MenuGroupLabel) — 1280 미만은 Menu Sheet | 대시보드 위젯 크기 — 편집 모드에서만 |
State
| 상태 | 모습 |
|---|---|
enabled | 선 stroke-neutral-weak + 손잡이 표시 stroke-neutral-solid |
hovered | 웹 — 선 전체가 stroke-neutral-solid · 커서 col-resize |
focused | 웹 — 누르는 폭 16 둘레에 링 2px(모서리 4) · 선이 짙어진다 |
pressed | 끄는 동안 — 선이 짙고, 화면 전체 커서 col-resize · 글을 고르지 않는다. 위젯은 자리표시 + 따라오는 위젯 |
상태 — 나누기
| 상태 | line 너비 | line 배경 | line 전환 대상 | line 전환 시간 | line 전환 곡선 | 링 두께 | 링 간격 | 링 색 | focusRing 모서리 |
|---|---|---|---|---|---|---|---|---|---|
enabled 기본 | 1px | color-stroke-neutral-weak #E5E8EF · 다크 #404757 — 흰 면 위 1.23 · 다크 1.56 — 경계일 뿐이고 손잡이는 가운데 표시가 알린다 | background-color | motion-duration-color-transition 150ms | motion-ease-easing cubic-bezier(0.35, 0, 0.35, 1) | — | — | — | — |
hovered 웹 — 손잡이에 마우스를 올림 | 1px | color-stroke-neutral-solid #767C8B · 다크 #838997 — 선 전체가 손잡이 표시와 같은 색 — 끌 수 있는 자리가 보인다 | background-color | motion-duration-color-transition 150ms | motion-ease-easing cubic-bezier(0.35, 0, 0.35, 1) | — | — | — | — |
focused 웹 — 키보드 포커스 | 1px | color-stroke-neutral-solid #767C8B · 다크 #838997 | background-color | motion-duration-color-transition 150ms | motion-ease-easing cubic-bezier(0.35, 0, 0.35, 1) | 2px | 0px — 누르는 폭 16 둘레 — 패널 높이 전체 | color-stroke-focus-ring #1A1F2E · 다크 #F5F6FA | radius-r1 4px |
pressed 끄는 동안(누르고 있는 동안) | 1px | color-stroke-neutral-solid #767C8B · 다크 #838997 | background-color | motion-duration-color-transition 150ms | motion-ease-easing cubic-bezier(0.35, 0, 0.35, 1) | — | — | — | — |
상태 — 위젯
| kind | hovered 웹 — 손잡이에 마우스를 올림 | focused 웹 — 키보드 포커스 | pressed 끄는 동안(누르고 있는 동안) |
|---|---|---|---|
split | line 배경 color-stroke-neutral-solid #767C8B · 다크 #838997 — 선 전체가 손잡이 표시와 같은 색 — 끌 수 있는 자리가 보인다 | line 배경 color-stroke-neutral-solid #767C8B · 다크 #838997 · 링 두께 2px · 링 간격 0px — 누르는 폭 16 둘레 — 패널 높이 전체 · 링 색 color-stroke-focus-ring #1A1F2E · 다크 #F5F6FA · focusRing 모서리 radius-r1 4px | group 커서 col-resize — 끄는 동안 화면 전체 — 글을 고르지 않는다(user-select none) · line 배경 color-stroke-neutral-solid #767C8B · 다크 #838997 |
widget | — | 링 두께 2px · 링 간격 2px · 링 색 color-stroke-focus-ring #1A1F2E · 다크 #F5F6FA — ⋮ 단추의 링(Button) — 모서리 손잡이는 키보드로 닿지 않는다(대안이 ⋮ "크기") | placeholder 보이기 보임 — 손잡이를 끄는 동안 — 놓을 칸을 칸 단위로 · ghost 보이기 보임 |
모션
| 전환 | 시간 | 곡선 | 대상 | 비고 |
|---|---|---|---|---|
선 — 색 | motion-duration-color-transition 150ms | motion-ease-easing cubic-bezier(0.35, 0, 0.35, 1) | background-color | 올리거나 초점 · 끄는 동안 선이 짙어진다 |
목록 폭 | — | — | width | 끄는 동안 포인터를 1:1 로 따라가고, 키보드 · 누르기는 바로 바뀐다 — 모션이 없다 |
Guidelines
데스크톱에서만
나누기는 768 이상에서만 쓴다 — 768 미만은 나누지 않고 역할 · 사용자는 목록 → 상세, 회사는 탭이다(지금과 같다). 손으로 끄는 패널을 폰에 두지 않는다 — 옆에 띄우는 작업은 Side Panel(1280 미만은 Bottom Sheet), 여러 줄 칸은 자동 높이다(Textarea — 끌어 늘이는 손잡이 없음).
끌지 않아도 된다
끌기는 지름길이다 — 손잡이를 한 번 누르면 25 ↔ 40, 키보드는 ← → 5% · Home 20% · End 40% · Enter 25 ↔ 40 이다. 위젯은 ⋮ → "크기" 에서 작게 — 4칸 · 보통 — 6칸 · 넓게 — 8칸 · 가득 — 12칸을 고른다(높이는 그대로). 메뉴는 실행만이라 체크 · 라디오를 두지 않고, 지금 크기 줄에는 설명 "지금 크기" 를 단다(Menu).
화면마다 폭을 기억한다
끌거나 누른 목록 폭은 화면마다 브라우저에 기억한다(localStorage — 키는 화면 이름) — 다시 열면 그 폭이다. 사이드바의 손으로 접은 상태처럼 사용자의 기기에 남긴다. 위젯 크기는 지금처럼 저장 단추로 서버에 저장한다.
글
손잡이의 이름은 바뀌는 것을 말한다 — "목록 너비". 위젯 ⋮ 는 "{위젯} 더보기", 크기 줄은 "작게 — 4칸" 처럼 크기와 칸 수를 함께 쓴다.
코드
레시피 recipes/shadcn/components/ui/resizable.tsx 를 쓴다(react-resizable-panels 위에 짜도 된다 — 손잡이 모양 · 키보드 · 누르기 · 기억은 레시피가 맞춘다). 아래 미리보기는 스펙 값으로 그린 모습이다.
ResizablePanelGroup— 나눈 자리.storageKey(화면마다 폭을 기억할 이름 — 필수)를 받고 가로로만 나눈다. 768 미만에서는 쓰지 않는다(화면이 목록 → 상세 · 탭으로 바꾼다).ResizablePanel— 목록 패널.defaultSize(기본 25) ·minSize(20) ·maxSize(40) ·id. 오른쪽 상세 패널은ResizablePanel하나 더 — 크기를 적지 않는다(남는 폭).ResizableHandle— 손잡이.label(이름 — 필수, "목록 너비") ·toggleSize(누르기 ·Enter로 오가는 넓은 폭 — 기본 40). 선 · 손잡이 표시 · 누르는 폭 16 ·role="separator"(aria-valuenow·aria-valuemin·aria-valuemax·aria-orientation·aria-controls) ·←→5% ·Home·End를 손잡이가 맡는다.WidgetResizeHandle— 위젯 모서리 손잡이(누르는 24 · 표식 9 꺾쇠). 대시보드 격자(react-grid-layout)의resizeHandle로 넘긴다 — 보조 기술에는 숨긴다. 끄는 동안 자리표시 · 따라오는 위젯의 모양은 레시피의 CSS 가 이 스펙 값으로 덮는다. ⋮ 의 "크기" 는 Menu 로 짠다.
HR 관리 — 부서 나무 | 조직도
import { ResizableHandle, ResizablePanel, ResizablePanelGroup } from "@/components/ui/resizable"
{/* 768 이상만 — 목록 20 ~ 40%(오른쪽은 60% 아래로 줄지 않는다), 화면마다 폭을 기억 */}
<ResizablePanelGroup storageKey="hr-admin-company">
<ResizablePanel id="company-tree" defaultSize={25} minSize={20} maxSize={40}>
{departmentTree}
</ResizablePanel>
<ResizableHandle label="목록 너비" toggleSize={40} />
<ResizablePanel>{orgChart}</ResizablePanel>
</ResizablePanelGroup>HR 대시보드 — 위젯 ⋮ 의 크기
import { EllipsisVertical } from "lucide-react"
import { Button } from "@/components/ui/button"
import { Menu, MenuContent, MenuGroup, MenuGroupLabel, MenuItem, MenuTrigger } from "@/components/ui/menu"
<Menu>
<MenuTrigger asChild>
<Button variant="ghost" layout="iconOnly" aria-label={`${widget.title} 더보기`}>
<EllipsisVertical />
</Button>
</MenuTrigger>
<MenuContent>
<MenuGroup>
<MenuGroupLabel>크기</MenuGroupLabel>
{/* 끌지 않는 대안 — 누르면 바로 그 폭, 높이는 그대로. 메뉴는 실행만이라 체크가 없다 */}
{[
{ label: "작게 — 4칸", columns: 4 },
{ label: "보통 — 6칸", columns: 6 },
{ label: "넓게 — 8칸", columns: 8 },
{ label: "가득 — 12칸", columns: 12 },
].map((size) => (
<MenuItem
key={size.columns}
label={size.label}
description={widget.columns === size.columns ? "지금 크기" : undefined}
onSelect={() => resizeWidget(widget.id, size.columns)}
/>
))}
</MenuGroup>
</MenuContent>
</Menu>Behavior
| 인터랙션 | 동작 |
|---|---|
| 손잡이 끌기 | 목록 폭이 포인터를 따라 20 ~ 40% 사이에서 바뀐다 — 놓으면 그 폭을 기억한다 |
손잡이 한 번 누르기(3px 안) · Enter | 기본 25 ↔ 넓게 40 |
← · → | 5% 좁히기 · 넓히기 — 끝에서 멈춘다 |
Home · End | 20% · 40% |
| 마우스 올리기 · 초점 | 선이 짙어진다 · 커서 col-resize |
| 다시 열기 | 그 화면에 기억한 폭 — 없으면 25 |
| 위젯 모서리 끌기(편집 모드) | 놓일 칸에 자리표시 · 위젯이 따라온다 — 놓으면 그 칸. 저장 단추로 서버에 |
| 위젯 ⋮ → 크기 | 고른 폭(4 · 6 · 8 · 12칸)으로 바로 — 높이는 그대로 · 초점은 ⋮ 로 |
Accessibility
| 기준 | 검증 |
|---|---|
| WCAG 1.4.11 Non-text contrast(≥ 3:1) | 손잡이 표시 · 위젯 표식 stroke-neutral-solid 흰 면 위 4.18 · 다크 4.13 ✓ · 자리표시 점선 stroke-brand-solid 바닥 위 HR 7.56 · 다크 6.88 ✓. 선 stroke-neutral-weak(1.23 · 1.56)는 경계일 뿐 — 손잡이는 표시가 알린다. 키보드 포커스 링 HR 8.17 · 6.07 ✓ |
| WCAG 2.1.1 Keyboard | 손잡이 ← → Home End Enter ✓ · 위젯 크기는 ⋮ 메뉴 ✓ |
| WCAG 2.5.7 Dragging Movements | 끌지 않고 손잡이를 한 번 눌러 25 ↔ 40 · 위젯은 ⋮ "크기" ✓ |
| WCAG 2.4.7 Focus visible | 누르는 폭 16 둘레 링 2px ✓ |
| WCAG 2.5.8 Target Size — Minimum(AA ≥ 24×24) | 손잡이 16 × 패널 높이 ⚠ — 데스크톱 마우스 경계(사용자 결정 12B), 같은 일을 키보드 · 누르기로 한다 · 위젯 모서리 24 × 24 ✓ · ⋮ 44 ✓ |
| WCAG 2.5.5 Target Size — Enhanced(AAA ≥ 44×44) | 손잡이 16 ✗ · 위젯 모서리 24 ✗ — 대안 ⋮ "크기" 44 ✓ |
| WCAG 4.1.2 Name, Role, Value | 손잡이 role="separator" · 이름 "목록 너비" · aria-orientation="vertical" · aria-valuenow(목록 %) · aria-valuemin 20 · aria-valuemax 40 · aria-controls(목록 패널) ✓. 위젯 모서리 손잡이는 마우스 · 터치용(보조 기술에 숨긴다) — 대안은 ⋮ |
| Reduced motion | 폭이 바뀌는 모션이 없다(끄는 동안 1:1 · 키보드 · 누르기는 바로) — 그대로 |
Do / Don't
✅ Do
- 768 이상 목록 | 상세에서 목록 폭만 — 20 ~ 40%, 오른쪽은 60% 아래로 줄지 않게.
- 손잡이 표시를 3 이상의 대비로 보이고, 이름 "목록 너비" 를 단다.
- 끌지 않는 길 — 한 번 누르기 25 ↔ 40 · 키보드 5% · 위젯 ⋮ "크기".
- 화면마다 넓힌 폭을 기억한다.
❌ Don't
- 폰에 끄는 패널 · 세로 나누기 · 셋 이상 나누기.
- 손잡이를 배경과 같은 색(1.11)으로 · 누르는 폭 5 · 이름 없는 손잡이.
- 오른쪽 패널을 0폭까지 밀기 · 키보드 한 번에 10%.
- 브랜드 색 손잡이 · 빨간 자리표시 · 끌기로만 바뀌는 위젯 크기.
Specification
resizable.yaml 의 규칙을 하나도 빼지 않고 조건마다 그린다 — HR 웹이 나누기 · 위젯 크기를 만들 때 이 값을 그대로 쓴다. 조건이 없는 Base 가 모든 조합에 걸리고, 뒤의 규칙이 앞의 같은 값을 덮는다. 상태는 enabled 에서 바뀌는 값만 적었다.
kind=split
| 부위 | 상태 | 속성 | 값 |
|---|---|---|---|
group | enabled | 나누는 방향direction | 가로목록 | 상세 — 세로 나누기 · 셋 이상 나누기는 쓰는 곳이 없어 두지 않는다 |
| 바뀌는 폭breakpoint | 768px 이상768 미만은 나누지 않는다 — 역할 · 사용자는 목록 → 상세, 회사는 탭(지금과 같다) | ||
panel | enabled | 너비width | 25%목록 패널의 기본 폭 — 1280 에서 본문 992 의 248(사이드바 240 · 여백 24 × 2) |
| 최소 너비minWidth | 20%목록이 이보다 좁아지지 않는다 | ||
| 최대 너비maxWidth | 40%오른쪽 상세가 60% 아래로 줄지 않는다 — 지금 회사 · 부서 사용자는 0폭까지 민다 | ||
| 누르면 넓게toggleWidth | 40%손잡이를 한 번 누르면(Enter) 기본 25 ↔ 넓게 40 | ||
| 폭 기억persist | 화면마다 기억끌거나 누른 폭을 브라우저에 저장한다(localStorage — 키는 화면: 역할 · 사용자 · 회사 · 부서 사용자). 사이드바 접힘처럼 다시 열면 그 폭 | ||
handle | enabled | 너비width | 16px누르는 폭 — 선 가운데로 양쪽 8. 768 이상 데스크톱의 마우스 경계라 44 가 아니다(사용자 결정 12B) |
| 커서cursor | col-resize | ||
| 터치 영역touchTarget | 16 × 패널 높이AA ⚠(폭 16) · AAA ✗ — 같은 일을 누르기(25 ↔ 40) · 키보드로도 한다 | ||
| 누름press | 누르면 25 ↔ 40끌지 않고 한 번 눌렀다 떼면(3px 안) 기본 25 ↔ 넓게 40 — WCAG 2.5.7 Dragging Movements 의 대안 | ||
| 키보드keyboard | ← → 5% · Home 20% · End 40% · Enter 25 ↔ 40APG Window Splitter — Shift + 화살표는 두지 않는다 | ||
| 이름name | 목록 너비role=separator · aria-orientation=vertical · aria-valuenow(목록 %) · aria-valuemin 20 · aria-valuemax 40 · aria-controls(목록 패널) | ||
| 터치 동작touchAction | none끄는 동안 화면이 스크롤되지 않게 | ||
| 끌기 시작dragStart | 3px누른 채 3px 넘게 움직이면 끌기 — 그 안에서 놓으면 누르기 | ||
| 한 번에step | 5%← → 한 번에 — 지금 HR 은 10% | ||
line | enabled | 너비width | 1px |
| 배경background | stroke-neutral-weak#E5E8EF다크 #404757흰 면 위 1.23 · 다크 1.56 — 경계일 뿐이고 손잡이는 가운데 표시가 알린다 | ||
| 전환 대상transitionProperty | background-color | ||
| 전환 시간transitionDuration | motion-duration-color-transition150ms | ||
| 전환 곡선transitionEasing | motion-ease-easingcubic-bezier(0.35, 0, 0.35, 1) | ||
grip | enabled | 높이height | 24px |
| 너비width | 4px | ||
| 모서리radius | radius-r0_52px | ||
| 배경background | stroke-neutral-solid#767C8B다크 #838997흰 면 위 4.18 · 다크 4.13(WCAG 1.4.11 ≥ 3) — 지금 HR 은 선 · 손잡이가 모두 #EEEEEE 1.11 | ||
| 자리position | 선 가운데패널 높이의 가운데 | ||
line | hovered | 배경background | stroke-neutral-solid#767C8B다크 #838997선 전체가 손잡이 표시와 같은 색 — 끌 수 있는 자리가 보인다 |
line | focused | 배경background | stroke-neutral-solid#767C8B다크 #838997 |
focusRing | focused | 링 두께width | 2px |
| 모서리radius | radius-r14px | ||
| 링 색color | stroke-focus-ringDesk#0147AD다크 #7AA9F6HR#075B3C다크 #2ABE84 | ||
| 링 간격offset | 0px누르는 폭 16 둘레 — 패널 높이 전체 | ||
group | pressed | 커서cursor | col-resize끄는 동안 화면 전체 — 글을 고르지 않는다(user-select none) |
line | pressed | 배경background | stroke-neutral-solid#767C8B다크 #838997 |
kind=widget
| 부위 | 상태 | 속성 | 값 |
|---|---|---|---|
group | enabled | 간격gap | spacing-x416px칸 사이 16 — 지금 HR 값 |
| 칸 수columns | 12대시보드 격자 — 지금 HR 값(react-grid-layout). 크기 · 위치는 편집 모드에서만 바꾸고 저장 단추로 서버에 저장한다 | ||
| 격자 한 줄rowHeight | 72px격자 한 줄 — 지금 HR 값 | ||
widgetHandle | enabled | 크기size | 24px누르는 자리 24 × 24 — 오른쪽 아래 모서리. 지금 20 |
| 커서cursor | se-resize | ||
| 터치 영역touchTarget | 24 × 24AA ✓ · AAA ✗ — 끌지 않는 대안은 ⋮ 의 "크기"(moreButton) | ||
| 터치 동작touchAction | none | ||
widgetMark | enabled | 크기size | 9px오른쪽 · 아래 두 변만 그린 꺾쇠 — 지금 5 |
| 테두리borderColor | stroke-neutral-solid#767C8B다크 #838997카드 면 위 4.18 · 다크 4.13(≥ 3) — 지금 rgba(0,0,0,.4) 는 2.85 · 다크 1.14 | ||
| 테두리 두께borderWidth | 2px | ||
| 오른쪽 거리right | 6px위젯 오른쪽 끝에서 | ||
| 아래 거리bottom | 6px위젯 아래 끝에서 | ||
placeholder | enabled | 모서리radius | radius-r416px위젯(Card 면)과 같은 16 |
| 배경background | bg-brand-weakDesk#E8F1FE다크 #20314EHR#E8F3EC다크 #21352B놓일 칸 — 브랜드 옅은 바탕. 지금은 라이브러리 기본 빨강 20% | ||
| 테두리borderColor | stroke-brand-solidDesk#0147AD다크 #7AA9F6HR#075B3C다크 #2ABE84바닥 위 HR 7.56 · 다크 6.88 · 옅은 바탕 위 7.18 · 5.47(≥ 3) — 자리표시의 경계 | ||
| 테두리 두께borderWidth | 1px비교 페이지 13A 그림은 1.5 였지만 크로미움은 1.5px 테두리를 1px 로 그린다(1 · 2배 화면 모두 — 레시피 하네스 실측) — 사용자가 본 모습 그대로 1 | ||
| 테두리 모양borderStyle | dashed | ||
| 보이기display | 숨김끄는 동안만 보인다 | ||
ghost | enabled | 모서리radius | radius-r416px |
| 그림자shadow | shadow-s30 8px 24px -4px rgba(15, 18, 28, 0.10), 0 2px 6px -2px rgba(15, 18, 28, 0.05) · 다크 0 8px 24px -4px rgba(0, 0, 0, 0.50), 0 2px 6px -2px rgba(0, 0, 0, 0.25), inset 0 1px 0 0 rgba(255, 255, 255, 0.08)떠 있는 동안만 | ||
| 보이기display | 숨김끄는 동안 포인터를 따라오는 위젯 — 위젯 모양 그대로 | ||
moreButton | enabled | 크기size | 40pxButton ghost · iconOnly · medium — 표 · 줄의 ⋮ 와 같다. 위젯 머리 오른쪽 |
| 아이콘 크기iconSize | 18pxlucide ellipsis-vertical | ||
| 터치 영역touchTarget | 44 × 44보이는 40 바깥으로 2 씩 — 모서리 손잡이(24)의 끌지 않는 대안. AA ✓ · AAA ✓ | ||
| 이름name | {위젯} 더보기편집 모드에서만 — 위젯 제목을 붙인다 | ||
sizeMenu | enabled | 항목items | 작게 — 4칸 · 보통 — 6칸 · 넓게 — 8칸 · 가득 — 12칸누르면 바로 그 폭 — 높이는 그대로. 메뉴는 실행만이라 체크 · 라디오가 없고, 지금 크기 줄에 설명 "지금 크기"(menu.md) |
| 글label | 크기메뉴 묶음 이름(MenuGroupLabel) — 1280 미만은 Menu Sheet | ||
focusRing | focused | 링 두께width | 2px |
| 링 색color | stroke-focus-ringDesk#0147AD다크 #7AA9F6HR#075B3C다크 #2ABE84⋮ 단추의 링(Button) — 모서리 손잡이는 키보드로 닿지 않는다(대안이 ⋮ "크기") | ||
| 링 간격offset | 2px | ||
placeholder | pressed | 보이기display | 보임손잡이를 끄는 동안 — 놓을 칸을 칸 단위로 |
ghost | pressed | 보이기display | 보임 |
SEED 와 다른 점
- SEED 에는 Resizable 이 없다 — 모바일 앱 중심이라 끌어서 나누는 패널이 없다. 손잡이 · 범위 · 키보드 · 기억 · 위젯 크기는 모두 groo 가 정했다(키보드는 APG Window Splitter).
Migration notes
2026-10-10 — 남기고 고친다
사용자가 펼치고 접기 · 나누기 비교 페이지에서 정했다 — HR 관리 화면 나누기는 남기고 고친다(12B — 선 stroke-neutral-weak + 손잡이 표시 대비 3 이상 · 누르는 폭 16 · 이름 "목록 너비" · ← → 5% · Home / End · 목록 20 ~ 40% · 오른쪽 60% 아래로 안 줄임 · 화면마다 폭 기억 · 손잡이 누르기로 25 ↔ 40), 대시보드 위젯 크기도 남기고 고친다(13A — 손잡이 대비 3 이상 · 자리표시 토큰 색 · ⋮ "크기"). 나누기를 걷는 안(12A)과 크기 고정(13B)은 고르지 않았다. 비교 페이지 그림의 "크기" 메뉴는 라디오 체크로 그렸는데, Menu 는 실행만이라(2026-10-02 결정) 지금 크기 줄에 설명 "지금 크기" 를 단다. 위젯 ⋮ 는 표 · 줄의 ⋮ 와 같은 Button ghost · iconOnly · medium(40 · 44)이다(그림은 36). 위젯을 옮기는 일의 끌지 않는 대안은 이번 묶음 밖이다. 옛 스펙은 resizable.history/v-pre-seed-collapse.* 에 남겼다.
| 옛 Resizable | 새 Resizable |
|---|---|
| 가로 · 세로 · 손잡이 상자(withHandle) 변형 | 가로 나누기 하나(768 이상) + 위젯 크기 |
선 1px border-default · 손잡이 상자 12 × 16 surface-default | 선 1px stroke-neutral-weak · 손잡이 표시 4 × 24 stroke-neutral-solid(3 이상) |
누르는 폭 4(::after) | 16 |
| hover · 끌기 모양 변화 없음 | 올리기 · 초점 · 끌기에 선이 짙어진다 |
| 키보드 라이브러리 기본(10%) · 링 1px | ← → 5% · Home · End · Enter 25 ↔ 40 · 링 2px |
| 크기 제한은 사용처 · 저장 언급 없음 | 목록 20 ~ 40% · 화면마다 기억 |
| (DESIGN v71) 손잡이 4 ~ 6px · hover 브랜드 색 · ±1% · snap 25 / 50 / 75% · "localStorage 권장" | 이 스펙으로 바꿨다 |
제품은 앱 적용 단계에서 옮긴다(2026-10-09 조사 — HR 은 1280 × 800 렌더로 쟀다). Desk 웹 · 앱 · SSO 에는 나누기가 없다.
- HR 손잡이(R1 ~ R4) — 선 · 손잡이 상자가 모두
bg-border#EEEEEE 라 패널 바탕 #FAFAFA 위 1.11(다크 1.34), 손잡이 상자 12 × 16 · 아이콘 10, 누르는 폭 5px, 이름 ·aria-orientation0, 키보드 한 번에 10%, hover · 끌기 모양 변화 0 이다(shared/ui/shadcn/resizable.tsx:42-49, D7). 회사 · 부서 사용자는 오른쪽 최소가 없어End한 번에 조직도 · 사용자 칸이 0폭이 된다(features/admin-company/ui/CompanyContent.tsx:116-140·features/admin-users-department/ui/DepartmentContent.tsx:137-178, D7). 넓힌 폭은 저장하지 않는다(autoSaveId0). 역할 · 사용자는 20 ~ 40% 가 맞다(features/admin-authority/ui/RoleManagementPanel.tsx:282-310·UserManagementPanel.tsx:124-152). - HR 대시보드 위젯(R5) — 손잡이 20 × 20 · 표식 5 × 5 꺾쇠 2px
rgba(0,0,0,.4)가 흰 카드 위 2.85 · 다크 1.14, 끄는 동안 자리표시가 라이브러리 기본 빨강 20%, 키보드로는 바꿀 수 없다(features/dashboard/ui/DashboardContent.tsx:206-240, D17). - 곁가지 — HR
package-lock.json의react-grid-layout1.5.3 은 npm 이 내려 둔 판이다("ships stray files … an ip_fetcher binary"). 1.5.4 는 그 파일만 뺀 같은 코드라 올린다 — 이 결정과 상관없이. - 스펙 글 — DESIGN.md v71 Resizable 이 이 스펙과 반대였다(조사 S7) — "펼치고 접기 · 나누기" 절로 바꿨다.
이전 기록
- 2026-05 — shadcn 그대로.
bg-border-default선 +surface-default손잡이 상자로 토큰 이름만 맞췄다. 이번에 모두 바뀌었다.