@olundot/ui 0.20.0-next.1 → 0.20.0-next.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,283 @@
1
1
  # @olundot/ui
2
2
 
3
+ ## 0.20.0-next.11
4
+
5
+ ### Minor Changes
6
+
7
+ - 708df3d: 떠 있는 `BulkActionBar`(`placement="floating"`, 기본값)를 알약 띠에서 카드로 바꾼다. 화면 폭과 관계없이 모양이 하나다.
8
+
9
+ - 1행: 개수(왼쪽, `role="status"` 그대로)와 선택 해제 ✕(오른쪽, ghost `IconButton` md). 2행: `summary`(있을 때만, 카드 안쪽 폭 전체, `keep-all`로 줄 바꿈, 자르지 않음). 3행: 작업 버튼(오른쪽 맞춤, 넘치면 다음 줄로 감싸며 오른쪽 맞춤 유지).
10
+ - 작업 버튼이 `Button` `appearance="outline"` `size="sm"`(30px)이 된다. 일반은 outline neutral, `variant: "destructive"`는 outline danger, 비활성은 Button의 disabled 꼴이다. 아이콘은 라벨 앞 그대로다. 터치 영역은 위아래로 4px씩만 넓혀(38px) 줄바꿈된 줄과 겹치지 않는다.
11
+ - 간격: 안쪽 여백 위 8 · 좌우 16 · 아래 12, 1행 32, 행 사이 8, 작업 줄 위 12, 버튼 사이 8. 모서리 `--radius-xl`. 폭 `min(360px, 자리 − 32px)`. 구분선이 없다.
12
+ - DOM 순서는 개수 → 작업 → ✕ 그대로다. ✕는 1행 오른쪽에 그려지지만 Tab으로는 맨 뒤다.
13
+ - 카드가 떠 있는 동안 문서 루트에 `--olun-bulk-bar-offset`(카드 높이 + 아래 간격 `16px + env(safe-area-inset-bottom)`)을 둔다. 언마운트, count 0으로 숨김, 높이 0(`display: none`)이면 지운다. 카드가 여럿이면 가장 큰 값이다.
14
+ - `ToastViewport`가 이 값보다 8px 위에 뜬다(값이 없으면 예전처럼 아래 24px).
15
+ - `DataLayout`의 `bulkBar` 자리가 아래 16px에 `env(safe-area-inset-bottom)`을 더한다. 떠 있는 카드와 같은 간격이다.
16
+
17
+ 호환성 영향:
18
+
19
+ - floating에서 `trailing`을 받지 않는다. 타입에서 막히고(`placement`가 `"inline"`일 때만 허용), 타입을 우회해 넘기면 그리지 않고 개발 모드에서 경고를 한 번 낸다. 편집·완료 같은 컨트롤은 도구 줄 오른쪽 끝에 둔다. `placement="inline"`의 `trailing`은 그대로다.
20
+ - `BulkActionBarProps`가 `placement` 유니언 타입이 되어 `interface X extends BulkActionBarProps`가 타입 오류를 낸다. `type X = BulkActionBarProps & { … }`로 바꾼다.
21
+ - floating에서 `data-slot="bulk-action-bar-divider"`와 `data-slot="bulk-action-bar-clear"`가 없어진다. ✕는 `IconButton`의 `data-slot="icon-button"`이고 `aria-label="선택 해제"`로 찾는다. 작업 버튼의 `data-slot="bulk-action-bar-action"`과 `data-variant`는 그대로다.
22
+ - 카드 높이가 예전 알약(약 42px)보다 크다. 목록 아래 여백을 카드 높이에 맞추거나 `--olun-bulk-bar-offset`을 쓴다.
23
+ - `placement="inline"`(사용 중단 예정)은 바뀌지 않는다.
24
+
25
+ ## 0.20.0-next.10
26
+
27
+ ### Minor Changes
28
+
29
+ - c5f25cb: ActionMenu 항목에 `disabledReason`을 추가했다. 주면 항목이 흐리게 보이지만 다른 항목처럼 포커스를 받고, 라벨 아래 사유 줄이 모든 폭에서 보인다(`aria-disabled`, 사유는 `aria-describedby`). 고르면 `onSelect`를 부르지 않고 메뉴가 열린 채 남는다.
30
+
31
+ - 1024px 이상 드롭다운, 1024px 미만 메뉴 시트, DataTable `rowActions`의 ⋮·카드 ⋮·우클릭 메뉴, ActionSheet가 같게 동작한다.
32
+ - `disabled`와 함께 주면 `disabledReason`을 따른다. `description`과 함께 주면 사유 줄만 보인다. `destructive`와 함께 주면 위험 색 대신 흐린 글자다.
33
+ - `disabled`와 `description`만 쓰는 항목은 동작이 같다.
34
+
35
+ - f89a2a6: `Alert`에 `action` 슬롯을 더한다. 제목 줄 끝, 닫기 버튼 앞에 놓인다. 설명이 있으면 제목 줄 위쪽에, 없으면 한 줄 가운데에 맞춘다.
36
+
37
+ `Alert`의 `children`이 선택이 된다. 없으면 본문 칸을 그리지 않고 표식, 제목, `action`, 닫기 버튼을 한 줄 가운데에 맞춘다. `title`은 그대로 필수다.
38
+
39
+ Alert 폭이 366px보다 좁으면 `action`이 글자 아래 줄로 내려간다. 컨테이너 쿼리라 화면 폭이 아니라 Alert 폭을 따른다. `action`을 주면 루트에 `@container`와 `w-full`이 붙어 Alert가 부모 폭을 다 쓴다. `action`이 없으면 예전처럼 내용 기준 폭이다.
40
+
41
+ 제목과 본문이 `word-break: keep-all`과 `overflow-wrap: break-word`로 줄을 바꾼다. 한글이 음절 사이에서 끊기지 않고, 공백 없는 긴 문자열은 넘치지 않고 나뉜다. 글자를 다듬거나 공백을 접지는 않는다.
42
+
43
+ 동작이 바뀐다: `error`의 기본 `emphasis`가 `strong`에서 `subtle`로 바뀐다. 모든 `variant`가 기본으로 상태 색 면 없이 그려진다. 면이 필요하면 `emphasis="strong"`을 준다.
44
+
45
+ 닫기 버튼은 보이는 크기 28px 그대로 누름 영역이 44px로 넓어진다. 표식, 글자, `action`, 닫기 사이 여백이 논리 방향이라 오른쪽에서 왼쪽으로 쓰는 화면에서도 누름 영역이 `action`이나 글자와 겹치지 않는다.
46
+
47
+ ## 0.20.0-next.9
48
+
49
+ ### Minor Changes
50
+
51
+ - 485f5fc: Deprecate BulkActionBar `placement="inline"`. Behavior is unchanged; it now logs a one-time development warning and is removed in the next minor. Use the default `floating` placement.
52
+
53
+ `compactSummary` is used only by `placement="inline"`, so it is deprecated too and removed with it.
54
+
55
+ - 266aa4d: DataTable에 `defaultSort`를 추가했다. 기본 정렬 열을 누르면 정렬 해제 없이 기본 방향과 반대 방향을 오간다. 예전에는 기본 정렬이 내림차순인 열이 `desc → 해제(=기본 desc)`로 돌아 화면이 바뀌지 않고 오름차순에 닿지 못했다.
56
+
57
+ - 다른 열은 그대로 `asc → desc → 해제` 순이다.
58
+ - 1024px 미만 정렬 메뉴와 `aria-sort`도 같은 순환을 따른다.
59
+ - `sortBy`가 없으면 열 머리와 정렬 버튼이 `defaultSort`를 보인다.
60
+ - `defaultSort`를 주지 않는 표는 동작이 같다.
61
+
62
+ - b7c220f: NotificationBadge: 역할 없는 `span`의 `aria-label`은 읽히지 않을 수 있어 이름을 화면에 보이지 않는 글자(`sr-only`)로 옮겼다. 보이는 숫자는 `aria-hidden`이다. `aria-label` prop은 그대로 그 글자를 바꾼다. 루트에 `aria-label` 속성이 더는 붙지 않는다.
63
+
64
+ Chip: `onClick`과 `pressed`가 없는 `variant="status-filter"`는 `span`으로 그린다. 읽기만 하는 라벨이 Tab 순서에 들어가지 않고 `aria-pressed="false"`도 없어진다. 모양은 그대로다. 호환성 영향: 그런 칩을 `button` 역할로 찾던 테스트는 칩을 찾지 못한다. `onClick`을 넘기지 않는 래퍼로 툴팁을 단 칩은 키보드 포커스를 잃는다. DS `TooltipTrigger asChild`는 `onClick`을 넘기므로 칩이 `button`으로 남는다. `onClick`이나 `pressed`를 준 칩은 `pressed={false}`여도 지금처럼 `button`이다.
65
+
66
+ Chip: 개수(`count`)를 보조 기술이 라벨과 함께 읽는다. 보이는 개수는 `aria-hidden`이고 같은 값을 `sr-only` 글자로 둔다. `removeLabel` 기본값을 `'Remove'`에서 `'제거'`로 바꿨다.
67
+
68
+ FilterToolbar: 루트에 `role="group"`을 붙여 `aria-label`이 이름으로 노출된다. `role="toolbar"`는 컨트롤 사이 화살표 키 이동을 약속하므로 쓰지 않는다. 사용하는 쪽이 준 `role`이 이긴다.
69
+
70
+ CheckboxGroup·RadioGroup: `orientation="horizontal"`이 실제로 가로 배치가 된다. `plain` 선택지가 줄 전체 폭(`w-full`) 대신 내용 폭이라 나란히 놓이고, 자리가 모자라면 줄을 바꾼다. `card`와 `vertical`은 그대로다.
71
+
72
+ DataTable: `density` prop을 사용 중단 예정으로 표시했다(JSDoc `@deprecated`). 값마다 정해진 디자인 값이 없어 지금도 효과가 없다. 동작은 그대로다. 호환성을 깨는 다음 릴리스에서 지운다.
73
+
74
+ - c1771fa: `FilterToolbar`의 `actions`를 결과 요약 줄에서 컨트롤 줄(`filter-toolbar-row`)로 옮겼다. 공개 API는 같고 배치만 바뀐다. `actions`는 컨트롤이 놓인 마지막 줄의 오른쪽 끝에 붙는다. 640px 미만에서 `filters`가 있으면 필터 다음 줄 오른쪽에 놓인다. 필터를 모두 `collapsible`로 접으면 검색, 접힌 필터 버튼, `actions`가 한 줄에 놓인다. `resultSummary`와 `clearAction`은 예전처럼 아래 줄에 남는다. `search`, `filters`, `collapsible`이 모두 없으면 `actions`는 예전처럼 `resultSummary`, `clearAction` 뒤 같은 줄에 놓인다. DOM 순서는 `search`, `filters`, `actions`, `resultSummary`, `clearAction`이다.
75
+
76
+ 640px 미만에서 검색은 같은 줄의 남은 폭을 쓴다. 검색의 최소 폭은 160px이되 툴바 폭을 넘지 않는다. 검색 옆에 들어가지 않는 `actions`는 다음 줄로 내려간다.
77
+
78
+ - 933260c: FormField가 자식 DS 폼 컨트롤에 라벨과 안내 연결을 컨텍스트로 내려 준다. 공개 prop은 그대로다.
79
+
80
+ - 자식을 요소로 주면 Input, Textarea, Select, SearchField, Checkbox, Switch가 FormField 컨텍스트를 받는다. 자기 `id`가 없으면 FormField의 `id`를 쓰고, `aria-describedby`는 직접 준 값, 컨트롤 자체 안내, FormField 안내 순으로 합친다. 래퍼 요소 안에 있어도 컨텍스트를 받는다.
81
+ - RadioGroup, CheckboxGroup, ModeToggle은 FormField 라벨을 `aria-labelledby`로, 안내와 오류를 `aria-describedby`로 받는다. OTPField는 FormField 안내와 오류만 합친다.
82
+ - 그룹 항목과 오버레이 콘텐츠 안 컨트롤은 바깥 컨텍스트를 받지 않는다. 오버레이는 Dialog, Sheet, AlertDialog, ActionMenu 시트, DropdownMenu, Tooltip, Popover, Select 옵션 목록이다. 오버레이 안의 FormField는 자기 컨텍스트를 쓴다.
83
+ - FormField 안 컨트롤은 내부 설명과 오류 id를 따로 만든다. 컨트롤에 FormField와 같은 `id`를 줘도 설명 id가 겹치지 않는다.
84
+ - 하나뿐인 자식이 `input`, `select`, `textarea`, `button`이면 FormField가 `id`와 `aria-describedby`를 넣는다. 자식에 직접 준 값이 먼저다. 그 밖의 자식에는 아무것도 넣지 않는다.
85
+ - `id`가 없어도 라벨이 `label` 요소로 그려지고 FormField가 만든 id를 가리킨다. 함수 자식의 `controlProps.id`, `descriptionId`, `errorId`도 이 id를 따른다.
86
+ - Checkbox, Switch, OTPField에 직접 준 `aria-describedby`가 이제 내부 설명 연결을 덮지 않고 합쳐진다.
87
+ - FormField 하나에는 DS 폼 컨트롤을 하나만 둔다. 둘을 두면 같은 id가 두 번 생긴다. DS 폼 컨트롤에 FormField와 다른 `id`를 주면 라벨이 그 컨트롤을 가리키지 않는다.
88
+
89
+ - 5e0483b: `InlineAlert`의 `surface`와 `emphasis` prop을 지운다. 호환성을 깨는 변경이다. 인라인 문구는 늘 면과 테두리 없이 그린다. 넘기던 곳은 prop을 지우고, 상자가 필요하면 `Alert`를 쓴다. `InlineAlertSurface` 타입, `data-surface`, `data-emphasis`도 없어진다.
90
+
91
+ `InlineAlert`에 `marker`를 둔다. 기본값 `icon`은 `Alert`와 같은 상태별 아이콘을 12px, 상태 색으로 글자 첫 줄 가운데에 그린다. 아이콘은 `aria-hidden`이다. 점을 유지하려면 `marker="dot"`을 준다. 표식과 글자 사이는 `--space-2`다.
92
+
93
+ ## 0.20.0-next.8
94
+
95
+ ### Minor Changes
96
+
97
+ - e537c1a: Add Select `labelPlacement="inline"` to show the label before the value inside the trigger while keeping menu options unchanged. The accessible name includes both the label and current value, and long values truncate without shrinking the label.
98
+
99
+ Existing default behavior is unchanged: `labelPlacement="top"` and `clearable=true`. Only inline label placement defaults `clearable` to false; an explicit `clearable` value always takes precedence. Correct the `clearable` JSDoc to match these defaults.
100
+
101
+ - 3ffb5c2: `TablePagination` 추가: 표 바닥 페이지 패널(행 수 선택 20·50·100, 범위 `1–100 / 총 N개`, ◀ ▶). `shouldHideTablePagination`, `TABLE_PAGE_SIZE_OPTIONS`도 함께 내보낸다. `DataTable`에 `footer` 슬롯을 더했다. 표 테두리 안 맨 아래에 위쪽 구분선과 함께 그리고, 1024px 미만 카드 목록에서도 같은 자리다. footer가 아무것도 그리지 않으면 구분선도 없다.
102
+ - 64dd310: `DataLayout`의 `header`를 선택으로 바꿨다. 앱 셸이 페이지 머리를 맡는 화면은 `header`를 넘기지 않는다. 넘기지 않거나 넘긴 머리가 아무것도 그리지 않으면(빈 Fragment, `null`을 돌려주는 컴포넌트) 머리 블록이 보이지 않고 위쪽 간격도 없다. 내용이 있는 머리는 예전과 같다. `DataLayout` 문서와 목록 화면 예제를 더했다.
103
+
104
+ `bulkBar`에 넣은 floating `BulkActionBar`가 자기 폭보다 32px 좁아져 작업 버튼이 세로로 꺾이던 결함을 고쳤다. 바를 그대로 넣거나 `div`로 감싸 넣어도 바는 내용 폭 그대로 가운데에 놓이고, 바만 누름을 받는다. 바 옆 빈자리를 누르면 아래 표가 눌린다. `BulkActionBar`가 아닌 바는 예전처럼 내용 폭으로 가운데에 놓이고 누름을 받는다.
105
+
106
+ - c583d87: `DataTableColumn.header`를 `string`으로 좁혔다. 호환성을 깨는 변경이다. 요소를 넘기던 머리는 타입 오류가 난다. 보이는 글자 없이 접근 이름만 둘 열은 새 `hideHeader: true`를 쓴다. 열 머리 글자에서 `uppercase`를 뺐다. 크기, 굵기, 색은 그대로다.
107
+
108
+ 칸이 넘친 내용을 칸에서 자른다(`overflow: clip`). 소비자 CSS가 칸 안 말줄임을 풀어도 옆 칸 위로 그려지지 않는다. 새 열 옵션 `wrap`은 그 열 칸을 말줄임 없이 어절 단위로 여러 줄 보이고, 행 높이는 가장 긴 칸을 따른다. `virtual`과 함께 쓰면 개발 모드에서 한 번 경고한다.
109
+
110
+ DataTable 행과 행 링크, ListCard(카드 전체와 제목 덮개), TreeView 노드의 포커스 링을 `ring-inset`에서 `inset-ring`으로 바꿨다. 토큰 테마의 `--color-inset` 때문에 `ring-inset`이 링 색까지 `--bg-inset`으로 덮어 포커스 링이 거의 보이지 않던 결함이다. 이제 `--focus-ring` 색으로 보인다.
111
+
112
+ 앱에서 옮길 때: JSX 숨김 머리(`<span className="sr-only">상세</span>`)는 `header: "상세"`, `hideHeader: true`로 바꾼다. 머리 줄 `normal-case` 덮어쓰기는 지운다. 대화상자의 칸 줄바꿈 덮어쓰기(`overflow-visible! text-clip! whitespace-normal!`)는 그 열의 `wrap: true`로 바꾼다.
113
+
114
+ - 63ceb92: `DataTable`에 행 메뉴 `rowActions`와 `getRowActionsLabel`을 더했다. `rowActions`는 행마다 ActionMenu 항목 배열이나 그룹 배열을 돌려준다. 이 목록 하나가 1024px 이상의 마지막 ⋮ 열(44px, 버튼 32px), 1024px 미만 카드의 ⋮, 행 우클릭 메뉴(1024px 이상)를 모두 연다. 우클릭 메뉴가 열린 동안 그 행은 `active` 꼴이다. ⋮을 눌러도 행 이동, 펼침, 선택은 일어나지 않는다. 항목이 없는 행은 ⋮을 그리지 않고 열 자리만 둔다. 펼침 열과 함께 쓰면 ⋮ 열이 맨 끝, 펼침 열이 그 앞이다.
115
+
116
+ - `rowActions`를 주면 `getRowActionsLabel`이 필수다. `onRowContextMenu`와 함께 주면 타입 오류다.
117
+ - `rowActions`가 있으면 `getMobileCardContent().actions`는 무시되고 개발 모드에서 한 번 경고한다. 카드 ⋮은 그룹을 구분선 없이 이어 보인다.
118
+ - 새 타입 `DataTableRowActions`를 내보낸다.
119
+
120
+ `selectable`과 함께 쓰는 `isRowSelectable`을 더했다. `false`인 행은 체크박스가 비활성(1024px 미만 카드 포함)이고, `onSelectionChange`로 넘기는 집합에 들어가지 않으며, 바깥 `selectedIds`에 있어도 선택 꼴로 그리지 않는다. 머리 전체 선택과 숨긴 라이브 영역의 개수는 고를 수 있는 행만 센다. 고를 수 있는 행이 모두 선택이면 체크, 일부면 반쯤이고, 다시 누르면 고를 수 있는 행만 해제한다. 고를 수 있는 행이 없으면 머리 체크박스가 비활성이다. 불러오지 않은 행의 id는 그대로 둔다.
121
+
122
+ 앱에서 옮길 때: `columns` 끝의 ⋮ 열과 그 칸 여백 상수(액션 칸 패딩 덮어쓰기)를 지우고, 그 열의 메뉴 항목을 `rowActions`로 옮긴다. ⋮ 버튼의 `aria-label`은 `getRowActionsLabel`로 준다. 카드의 `getMobileCardContent().actions`와 우클릭용 `onRowContextMenu` + `ContextMenu` + `activeRowId` 조립도 지운다. 선택을 막으려고 머리 체크박스 클릭을 가로채던 코드는 `isRowSelectable`로 바꾼다.
123
+
124
+ ## 0.20.0-next.7
125
+
126
+ ### Minor Changes
127
+
128
+ - dcd6c46: `Alert`의 기본 표식이 상태 점에서 상태별 아이콘으로 바뀐다. `info`는 `Info`, `success`는 `CircleCheck`, `warning`은 `TriangleAlert`, `error`는 `CircleAlert`이고 16px, 상태 색이다. 아이콘은 `aria-hidden`이다. 점을 유지하려면 `marker="dot"`을 준다. `AlertMarker` 타입을 내보낸다.
129
+
130
+ `Alert`의 `title`이 필수다. 호환성을 깨는 변경이다. `title` 없는 `Alert`는 타입 오류가 난다. `InlineAlert`는 그대로다.
131
+
132
+ 배치를 Figma Alert에 맞췄다. 여백은 위아래 8px, 좌우 10px, 모서리는 `--radius-md`다. 제목은 14px `--fw-medium`, 줄 높이 21px이고 표식은 제목 첫 줄 가운데에 놓인다. `subtle` 본문은 `--fs-sm`, `--text-secondary`다. 닫기 버튼은 28px이고 `--bg-surface` 면과 `--border` 테두리를 갖는다.
133
+
134
+ - ffda0cc: 겹침 창 제목을 16px, 굵기 600, 줄 높이 1.5로 통일했다. AlertDialog, ConfirmDialog, 구조형 Dialog와 Sheet는 18px·700에서, `DialogTitle`은 20px·700에서 바뀐다. `SheetTitle`은 크기를 상속하던 것을 16px로 정했다. ActionMenu 메뉴 시트 제목은 15px·600 그대로다.
135
+
136
+ 앱에서 `DialogTitle`이나 `SheetTitle`에 준 `className="text-base font-semibold"`는 지워도 계산 값이 같다.
137
+
138
+ ## 0.20.0-next.6
139
+
140
+ ### Minor Changes
141
+
142
+ - 3d64e18: `DataTable`에 행 펼침을 더했다.
143
+
144
+ - `renderExpanded(row)`: 펼친 내용을 그린다. 처음 펼칠 때 그리고 접어도 남겨 두어 닫을 때도 높이가 전환된다.
145
+ - `getExpandLabel(row)`: 펼침 버튼과 펼치는 카드 제목의 접근 이름이다. `renderExpanded`와 함께 필수다.
146
+ - `expandedIds`, `onExpandedChange`: 펼친 행의 `keyField` 값이다. `expandedIds`가 없으면 표가 상태를 가진다. 여러 행을 함께 펼칠 수 있다.
147
+ - 1024px 이상: 표 끝에 40px 열을 붙이고 32px 펼침 버튼(`IconButton` md, ghost·neutral, `chevron-right`)을 둔다. 펼치면 화살표가 90° 돈다. 행의 빈 곳을 눌러도 펼친다. 펼친 내용은 바로 아래 행 하나에 표 폭으로 그린다.
148
+ - 1024px 미만: 카드 제목이 펼침 버튼이 되고 그 ::after가 카드를 덮는다. `value` 뒤, ⋮이 있으면 그 아래에 도는 화살표를 둔다. 펼친 내용은 카드 안 아래쪽에 그린다. ⋮과 행 선택 체크박스는 제목 밖에서 따로 눌린다.
149
+ - 높이는 `grid-template-rows` `0fr`→`1fr`로 `--dur-base` 동안 바뀐다. 접힌 내용은 `visibility: hidden`이라 포커스와 클릭을 받지 않는다. 동작 줄이기 설정에서는 전환이 없다.
150
+ - `rowHref`, `linkComponent`, `onRowClick`, `virtual`과는 함께 쓸 수 없다. 타입이 막는다. `selectable`과는 함께 쓴다.
151
+ - `DataTableProps`는 이제 두 갈래(이동, 펼침)의 유니온 타입이다. `interface … extends DataTableProps<T>`로 넓히던 코드는 `type … = DataTableProps<T> & …`로 바꾼다. `Partial<DataTableProps<T>>`를 펼쳐 넘기던 코드는 `renderExpanded`가 선택값이 되어 타입 오류가 날 수 있다.
152
+
153
+ - 08232da: `EmptyState`에 `layout`을 더했다. 기본값 `inline`은 지금과 같다.
154
+
155
+ - `layout="page"`는 화면 본문 전체를 대신하는 빈 상태다. 폭은 부모를 채우고 내용은 가로 가운데에 둔다.
156
+ - 768px 이상에서는 가장 가까운 세로 스크롤 영역의 남은 높이만큼 늘어나 내용을 세로 가운데에 둔다. 위아래 형제와 여백을 뺀 높이라 스크롤이 생기지 않는다. 내용이 이미 넘치면 늘지 않는다.
157
+ - 768px 미만에서는 위쪽에 붙이고 위 여백을 `--space-12`로 둔다.
158
+ - 앱에서 화면 단위 빈 상태를 가운데에 두던 `flex min-h-[50vh] items-center justify-center` 래퍼는 지우고 `layout="page"`를 쓴다.
159
+
160
+ - ea4266c: `bg-inset` 바탕 위 neutral 표식이 바탕과 같은 색이 되지 않게 했다. 공개 prop은 그대로다.
161
+
162
+ - 채움이 `--fill-on-bg`를 따른다: Badge `neutral`, Chip 개수(`neutral`, `muted`), TwoTierBadge `neutral` 채움. 값이 없으면 예전 채움을 쓴다.
163
+ - `--fill-on-bg`를 넘기는 곳: `contained` Tabs 목록, ModeToggle, 눌린 `muted` Chip, `filled` + `neutral` Button과 IconButton, 파일이 든 FileUploadZone. 앱의 `bg-inset` 유틸리티도 같다.
164
+ - 되돌리는 곳: `surface` 공통 배경(Card, DataTable, EmptyState 등), `neutral` Chip, `outline` Button과 IconButton.
165
+ - 라이트의 inset 바탕 위 Badge `neutral`은 #F1F1F2에서 #FFFFFF로 바뀐다. 다크의 Chip 개수는 #16181D에서 #26272B로 바뀐다. inset 바탕 밖은 바뀌지 않는다. 눌린 `muted` Chip의 개수는 예외로, 칩 자체가 inset이라 라이트에서 흰 채움이 된다.
166
+ - `bg-[var(--bg-inset)]` 같은 임의값 바탕은 넘기지 않는다. 앱은 `bg-inset` 유틸리티를 쓴다.
167
+
168
+ - 9f80206: `Switch`에 `labelPlacement`와 `fullWidth`를 더한다. `labelPlacement="start"`는 라벨을 앞에, 트랙을 뒤에 둔다. 기본값 `end`는 지금과 같다. `fullWidth`를 주면 행이 부모 폭을 채우고 트랙이 오른쪽 끝에 놓인다. 라벨이 길면 라벨이 줄을 바꾸고 트랙은 첫 줄 오른쪽 끝에 남는다. 설명은 라벨 아래에서 라벨과 같은 선에서 시작한다.
169
+
170
+ 라벨과 설명은 어절 경계에서만 줄을 바꾼다(`word-break: keep-all`). 두 배치 모두에 적용되어, 좁은 칸에서 단어 중간이 끊기던 기존 사용처의 줄바꿈도 바뀐다.
171
+
172
+ - 3ff068d: `Tabs`의 `TabItem.badge`를 건수 알약으로 바꿨다. 공개 API는 늘지 않는다. `line`, `contained`, 세로 방향 모두 해당한다.
173
+
174
+ - 모양: 테두리가 없어지고, 채움은 `--action-selected` 반투명 겹침이라 바탕을 따라간다. 숫자는 탭 글자색을 따른다. 높이는 18px에서 16px로 줄고 글자 굵기는 600에서 500으로 바뀐다. 선택된 탭의 테두리 강조는 없어진다.
175
+ - 1000부터 `999+`로 보인다. 예전에는 100부터 `99+`였다.
176
+ - 0도 보인다. 예전에는 0을 숨겼다. 숨기려면 값을 넘기지 않는다. 음수와 `NaN`은 보이지 않는다(개발 모드에서 경고).
177
+ - 탭 접근 이름에 실제 수가 들어간다. "감지 내용 12"처럼 읽고, `999+`로 보이는 탭도 "학생 1234"처럼 실제 수로 읽는다. 보이는 알약은 그대로 `aria-hidden`이다.
178
+ - 알약 글자 굵기는 고정이라 선택이 바뀌어도 탭 폭이 그대로다.
179
+
180
+ 탭 목록이 넘치면 가로 스크롤은 그대로 되고 스크롤바만 숨긴다. 키보드로 고른 탭이 보이는 자리로 스크롤되는 동작은 그대로다.
181
+
182
+ ### Patch Changes
183
+
184
+ - Updated dependencies [ea4266c]
185
+ - @olundot/tokens@0.18.0-next.3
186
+
187
+ ## 0.20.0-next.5
188
+
189
+ ### Minor Changes
190
+
191
+ - a0b298b: `Button`·`IconButton`·`CopyButton`의 옛 `variant`를 지운다. 호환성을 깨는 변경이다. `appearance`와 `tone`으로 옮긴다(`primary`·`default` → `filled` + `primary`, `secondary`·`outline`·`subtle` → `outline` + `neutral`, `ghost` → `ghost` + `neutral`, `danger`·`destructive`·`emergency` → `filled` + `danger`). `ButtonVariant`·`IconButtonVariant` 타입과 `buttonVariants`의 `variant` 인자도 없어진다.
192
+
193
+ `Button`의 `leadingIcon`·`trailingIcon`과 `size="icon"`·`"icon-xs"`를 지운다. 아이콘이 있는 버튼은 글자가 있는 `IconButton`(`icon`, 끝 아이콘은 `iconPosition="end"`)으로, 아이콘만 있는 버튼은 `IconButton` `lg`·`md`로 옮긴다.
194
+
195
+ `IconButton`은 옛 경로가 없어진다. `appearance`와 `tone`을 비워도 `ghost` + `neutral`이고 `size`는 늘 `Button`과 같은 5단계다. 옛 `sm`(28px)은 `xs`, 옛 `lg`(40px)는 `xl`로 옮긴다. 그대로 두면 30px, 36px가 된다. 아이콘 획 굵기도 같은 크기의 `Button`을 따른다. `data-variant`가 없어진다.
196
+
197
+ 글자가 있는 `IconButton`은 같은 `size`의 `Button`과 모양, 상태가 같다. 눌림 축소, 포커스 테두리와 2px 그림자, 비활성 테두리, 전환 속성이 `Button`과 같아지고 `shrink-0`이 빠진다. `IconButton`에 `loading`을 더한다. 회전 표시가 아이콘 자리를 대신하고 `aria-busy`, `aria-disabled`, 비활성 상태가 된다.
198
+
199
+ 새 조합 `outline` + `danger`를 연다. `--bg-surface` 바탕에 `--error` 테두리와 글자, hover는 `--error-bg`다. `Button`과 `IconButton`이 같은 칠을 쓴다. 하단 막대와 푸터에서 주 작업 옆의 위험 작업에 쓴다.
200
+
201
+ `Button`에 `appearance="link"`를 더한다. `variant="link"`를 대신하고 `Button`에만 있다. `tone`은 `primary`(`--accent` 글자, 기본값)와 `danger`(`--error` 글자)다. 밑줄은 hover 때만, 방문 표시는 없다. 가로 여백은 0이다. 전에는 크기의 여백이 `px-0`을 이겼다. 비활성이면 바탕 없이 글자만 흐려진다. `buttonVariants({ appearance: "link" })`도 같은 클래스를 돌려준다.
202
+
203
+ `Button asChild`가 자식 배열 때문에 `React.Children.only` 오류를 내던 것을 고친다. `Slot.Slottable`로 자식 하나를 그리고 회전 표시와 메뉴 표시를 그 안에 넣는다. `disabled`·`loading`이면 자식에 `aria-disabled`, `tabIndex={-1}`를 달고 클릭과 가운데 클릭을 막는다. `href`는 자식 것이라 지우지 않는다. 이동은 `<Button asChild appearance="link"><Link href>…</Link></Button>`로 그린다.
204
+
205
+ `Button`과 글자가 있는 `IconButton`에 `menuIndicator`를 더한다. 메뉴를 여는 버튼 끝에 `aria-hidden` ▾(`ChevronDown`)를 붙인다. 크기와 획은 버튼 크기를 따른다. `IconButton`에서 `iconPosition="end"`나 아이콘만 있는 버튼과는 타입이 막는다. `aria-haspopup`은 트리거가 단다.
206
+
207
+ `CopyButton`은 `variant` 대신 `appearance`·`tone`을 받는다. 기본값은 `outline` + `neutral`로 전과 같다.
208
+
209
+ `ActionFooterAction`의 `variant`를 `appearance`·`tone`으로 바꾼다. 위험 작업 묶음은 `tone="danger"`로 정한다. `leadingIcon`이 있는 작업은 글자가 있는 `IconButton`으로 그린다.
210
+
211
+ `scripts/codemods/button-020.mjs`가 아이콘 `Button`을 글자가 있는 `IconButton`으로, `CopyButton`과 그 자리의 `ActionFooter` `actions` 리터럴 `variant`를 새 축으로 옮긴다. 리터럴 `variant="link"`는 `appearance="link"`로, `*MenuTrigger asChild` 바로 안의 끝 `ChevronDown`은 `menuIndicator`로 옮긴다.
212
+
213
+ - 71ce3f9: `EmptyState`의 `bordered` 카드 크기를 정한다. 폭은 부모를 채우되 560px까지이고 가운데에 놓인다. `md`는 높이가 240px 아래로 줄지 않는다. `sm`과 `lg`는 내용 높이를 따르고, `bordered`가 아니면 달라지지 않는다.
214
+ - 141c7f5: `DataTable` 1024px 미만 카드와 `ListCard`에 행 메뉴를 더하고 아이콘 자리와 메타 줄을 바꿨다.
215
+
216
+ - `DataTableMobileCardContent.actions`, `ListCard`의 `actions`(`ListCardActions`): `{ items, label }`을 받아 ⋮ 버튼(`IconButton` md, ghost·neutral)과 `ActionMenu`를 그린다. 1024px 미만에서는 메뉴 시트로 열린다. `label`은 ⋮ 버튼의 접근 이름이다.
217
+ - 아이콘 자리: 아이콘이 하나면(⋮만, 또는 이동 화살표만) 오른쪽 위, 제목 줄에 둔다. ⋮과 이동 화살표가 같이 있을 때만 이동 화살표를 오른쪽 아래로 내리고 카드 최소 높이를 84px로 둔다. 이동 화살표가 세로 가운데에서 오른쪽 위로 옮겨 간다.
218
+ - 아래 줄의 날짜(`date`, `ListCard`의 `caption`)를 `status`에서 떼어 냈다. 상태 배지나 날짜 중 하나만 있어도 아래 줄을 그린다.
219
+ - `DataTableMobileCardContent.ariaLabel`: 제목이 글자(`string`, `number`)면 생략할 수 있고, 생략하면 제목 글자가 카드의 접근 이름이 된다. 제목이 다른 노드면 타입이 `ariaLabel`을 요구한다.
220
+ - `value`는 오른쪽 윗줄, 아이콘 왼쪽에 둔다. 앞 요소(`leading`, 행 선택 체크박스)는 제목 줄에 맞춘다.
221
+ - 메타는 묶음 하나를 한 줄로 그린다. 값 사이는 `·` 대신 12px 간격이고, 값 중간에서는 줄을 바꾸지 않는다. 스크린 리더가 값을 끊어 읽도록 값 사이에 숨긴 쉼표를 둔다. `meta` 타입은 그대로다.
222
+ - `onRowClick`이나 링크 없는 `rowHref` 카드는 제목이 `button`이 되고, 그 ::after가 카드 전체를 덮는다. 목록 항목(`listitem`)은 더 이상 클릭과 포커스를 받지 않는다. 행 선택 체크박스와 ⋮은 제목 버튼 밖에 놓여 따로 눌리고, ⋮ 메뉴 선택이 `onRowClick`으로 번지지 않는다.
223
+ - `ListCard`에 `actions`와 `href`나 `onClick`을 같이 주면 카드 전체 대신 제목이 링크나 버튼이 된다. `actions`가 없으면 전과 같다.
224
+
225
+ ## 0.20.0-next.4
226
+
227
+ ### Minor Changes
228
+
229
+ - 7f40e11: `ActionFooter` 배치를 바꿨다. 저장 상태가 남는 폭을 채우지 않고 주요 작업 묶음 바로 앞에 붙는다.
230
+
231
+ - 640px 이상: 위험 작업 묶음은 시작 쪽, 저장 상태와 주요 작업 묶음은 끝 쪽에 한 줄로 놓는다.
232
+ - 640px 미만: 넘칠 때만 줄을 바꾸던 배치 대신 두 줄로 고정한다. 윗줄은 위험 작업 묶음과 저장 상태, 아랫줄은 주요 작업 묶음이다. 주요 작업 버튼은 같은 폭으로 아랫줄을 채운다. 위험 작업과 저장 상태가 모두 없으면 윗줄을 두지 않는다.
233
+ - DOM 순서와 포커스 순서는 그대로다.
234
+
235
+ - 1b23296: DataTable의 좁은 화면 카드를 새로 공개한 `ListCard`로 바꿨다.
236
+
237
+ - **breaking**: `getMobileCardContent(row)`가 필수 prop이다. 반환 객체 `{ title, ariaLabel, leading?, meta?, status?, date?, value? }`가 카드 하나를 정한다. 첫 열과 나머지 열로 그리던 label:value 카드와 그 `data-slot`(`data-table-mobile-card-primary`, `-field`, `-select`)은 지웠다. 카드의 접근 이름은 `ariaLabel`이다.
238
+ - 표와 카드가 바뀌는 경계를 `md`에서 `lg`로 옮겼다.
239
+ - 좁은 화면 정렬은 목록 위 오른쪽의 정렬 버튼 하나다. 누르면 정렬 가능한 열의 라디오 메뉴가 열리고, 같은 열을 다시 고르면 오름차순, 내림차순, 정렬 해제 순으로 바뀐다. 접근 이름은 "정렬 학번, 오름차순" 꼴이다. 옛 정렬 토글 줄(`data-table-mobile-sort-toolbar`)은 지웠다.
240
+ - 선택 가능한 표는 카드 위에도 전체 선택 체크박스를 둔다.
241
+ - `virtual` 없이도 마운트 뒤에는 CSS가 보이는 쪽(표나 카드)만 행을 그린다. 서버 렌더와 첫 페인트는 그대로 둘 다 그린다. 경계를 넘을 때 포커스는 같은 행으로 옮긴다.
242
+ - `virtual` 카드 목록은 카드마다 높이를 재고, 안 잰 카드는 잰 카드 평균으로 어림한다.
243
+ - `align: "right"` 셀이 더는 흐려지지 않는다. 열 머리는 그대로다.
244
+ - `ListCard`: 제목, 메타 줄 2개, 상태 배지와 날짜의 아래 줄, 오른쪽 주 값(`value`)과 이동 화살표. 상태가 없으면 `caption`을 그리지 않는다.
245
+
246
+ - 1b23296: Use the 1024px desktop boundary for overlays, forms, and responsive page layouts. Share the overlay media query between menu presentation and swipe dismissal.
247
+
248
+ ### Patch Changes
249
+
250
+ - 1f6ee84: `Button` 라벨 줄 높이를 아이콘 크기와 같게 한다. `xs`는 14px, `sm`·`md`는 16px, `lg`·`xl`은 18px이다. 이전에는 라벨이 바깥 본문의 줄 높이를 물려받아 아이콘보다 줄 상자가 컸다. 버튼 높이와 글자 크기는 그대로다. 글자의 세로 위치는 0.5px 안에서만 달라진다.
251
+ - 52a99dc: 컨트롤 여백을 폐기된 `--pad-*` 유틸리티 대신 같은 값의 `--space-*`로 쓴다. Button, Input, Textarea, Select, Checkbox, RadioGroup, Card, Tabs의 값이 그대로라 화면 변화는 없다.
252
+ - Updated dependencies [1b23296]
253
+ - @olundot/tokens@0.18.0-next.2
254
+
255
+ ## 0.20.0-next.3
256
+
257
+ ### Minor Changes
258
+
259
+ - b7176ad: `OlunLogo`가 `@olundot/tokens/brand`의 정본 데이터만 그린다. 모양이 바뀐다.
260
+
261
+ - 워드마크: Pretendard 800 글자(`<span>`) 대신 Montserrat 700 윤곽 SVG(자간 +0.03em). 접근 가능한 이름은 `role="img"` + `aria-label`(`OLUN`, 워드마크만이면 `OLUN.`)이다.
262
+ - MASTER(기본형): SVG 하나로 그리고 `symbolSize` 하나로 통째로 확대·축소한다. 간격 12px(`--space-3`) 대신 대문자 높이 H 기준 고정 비율(링 바깥 지름 1.24H, 궤도 점 끝 → O 0.55H)을 쓴다. 높이는 잉크에 맞춰 예전 심볼 상자보다 낮다(기본 `symbolSize` 28에서 약 17px).
263
+ - `fontSize`: 워드마크만 쓸 때(`showSymbol={false}`)의 글자 크기로만 쓴다. MASTER에서는 무시한다.
264
+ - 워드마크만 쓸 때 마침표 색이 `--accent`에서 `--accent-pop`으로 바뀐다(궤도 점과 같은 색 표식 `olun-accent`).
265
+ - `OlunSymbol`: 모양과 크기는 그대로다.
266
+
267
+ ### Patch Changes
268
+
269
+ - 15f638e: 겹친 오버레이를 한 동작으로 함께 닫아도 포커스가 `<body>`로 떨어지지 않고 바깥 오버레이를 연 요소로 돌아간다. 바깥 오버레이가 먼저 사라지는 동안 안쪽 오버레이가 닫히는 애니메이션 중이어도 해당한다. Trigger가 있는 경우와 닫히는 동안 포커스가 다른 요소로 옮겨 간 경우는 그대로다.
270
+ - Updated dependencies [b7176ad]
271
+ - @olundot/tokens@0.18.0-next.1
272
+
273
+ ## 0.20.0-next.2
274
+
275
+ ### Patch Changes
276
+
277
+ - 48c5a4d: `Tabs` `line`의 선택 표시가 다른 탭을 고를 때 그 탭으로 미끄러진다(`--dur-base`, `--ease-standard`). 처음 그릴 때와 크기가 바뀔 때는 바로 놓이고, 동작 줄이기 설정에서는 움직이지 않는다. 가로와 세로 모두 해당하며 `contained`는 그대로다.
278
+ - 목록 안에 `data-slot="tabs-indicator"` 요소(`aria-hidden`) 하나가 생긴다. 하이드레이션 전에는 기존 `::after` 밑줄이 보이고, 강조선이 자리를 잡으면 숨는다.
279
+ - 선택된 탭 라벨이 굵어져도 탭 폭이 바뀌지 않는다. 라벨에 굵기별 숨은 사본을 두어 폭을 미리 잡는다. 탭을 바꿔도 다른 탭이 밀리지 않는다.
280
+
3
281
  ## 0.20.0-next.1
4
282
 
5
283
  ### Minor Changes