sellmate-design-system-react 6.1.0 → 7.0.0

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.
@@ -269,7 +269,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
269
269
 
270
270
  | 스크롤 종류 | 어떻게 |
271
271
  | --- | --- |
272
- | **패널 자체 스크롤** (좌측 목록, 중앙 대화 등) | 그 패널 안쪽 하단에 `pb-sd-80` |
272
+ | **패널 자체 스크롤** (좌측 목록, 중앙 대화 등) | 그 패널 안쪽 하단에 `pb-[var(--cmp-pageBody-padding-scrollEnd)]` — `SPage` 와 같은 토큰이라 값이 바뀌어도 함께 따라간다 |
273
273
  | **페이지 단위 스크롤** | **`SPage` 가 넣는다. 직접 주지 않는다** |
274
274
 
275
275
  `SPage` 는 기본으로 넣으므로 **아무것도 하지 않으면 맞다.** 끄는 경우는 하나뿐이다 — **페이지네이션이 붙은 테이블.** 페이지네이션이 이미 "여기서 끝"을 알려주므로 `scrollEndSpacing={false}` 로 끈다 (§4-2 목록 페이지).
@@ -413,7 +413,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
413
413
 
414
414
  - 작은 창이 아니라 **화면 하나가 통째로** 들어간다 — 검색 필터·테이블·페이지네이션이 그대로 있는 목록(`엑셀 파일 관리`), 헤더 카드·탭이 있는 상세(`이동 오더 상세`).
415
415
  - 상세를 팝업으로 여는 이유는 **목록을 떠나지 않고 여러 건을 번갈아 보기** 위해서다. 단 상세가 **항상** 팝업인 것은 아니고, 데이터 양이 많을 때 팝업을 쓴다.
416
- - 구조는 헤더(제목 중앙) + 본문 + 푸터다. **확정할 작업이 있으면 푸터에 `저장`, 조회만이면 `useFooter={false}`** 로 푸터를 없앤다.
416
+ - 구조는 헤더(제목 중앙) + 본문이고 **푸터는 기본으로 없다**. 조회만 하는 팝업은 그대로 두고, **확정할 작업이 있을 때만 `useFooter` 로 푸터를 켜서 `submitButton` 에 `저장`** 을 둔다.
417
417
  - **본문 패딩은 `SPopup` 이 토큰으로 넣는다. 직접 주지 않는다** (`p-sd-*` 로 덮어쓰면 토큰이 바뀌어도 안 따라간다). 표를 가장자리까지 채우는 등 콘텐츠가 여백을 직접 다뤄야 할 때만 `noPadding` 으로 끈다.
418
418
  - 그 밖에는 팝업 안도 일반 페이지와 같은 규칙(§4)을 따른다: 블록 간격 `gap-sd-12`, 표는 `SKeyValueTable` / `STable`.
419
419
 
@@ -430,7 +430,7 @@ function openDetailPopup(orderId: string) {
430
430
  // 2) 그 라우트의 루트에 SPopup 을 둔다 (조회만 → 푸터 없음)
431
431
  export default function TransferOrderPopupPage() {
432
432
  return (
433
- <SPopup popupTitle="이동 오더 상세" useFooter={false}>
433
+ <SPopup popupTitle="이동 오더 상세">
434
434
  {/* 본문 패딩은 SPopup 이 토큰으로 넣는다 — p-sd-* 로 덮어쓰지 않는다 */}
435
435
  <div className="flex flex-col gap-sd-12">
436
436
  <SSectionHeaderCard>…</SSectionHeaderCard>
@@ -493,7 +493,9 @@ SModal.create({ component: OrderModal, componentProps: { orderId } })
493
493
 
494
494
  작성 중인 내용이 있을 때 닫기를 시도하면 이탈 안내를 띄우는 것은 **소비 앱 몫**이다. 디자인 시스템은 dirty 상태를 알지 못하므로 백드롭·ESC 를 일괄 차단할 뿐이다. 안내가 필요하면 앱이 자체 dirty 판정 후 `SModal.confirm` 으로 띄운다.
495
495
 
496
- ### 3-4. 테이블 컬럼 정렬
496
+ ### 3-4. 테이블 컬럼 — 정렬과 너비
497
+
498
+ #### 정렬
497
499
 
498
500
  **값의 크기를 비교하는 숫자 컬럼은 예외 없이 오른쪽 정렬한다** (`align: 'right'`).
499
501
  자릿수가 세로로 맞아야 값의 크기를 눈으로 비교할 수 있기 때문이다.
@@ -537,6 +539,41 @@ const columns: STableColumn[] = [
537
539
 
538
540
  - `SKeyValueTable` 의 값 셀도 같은 기준을 따른다.
539
541
 
542
+ #### 컨트롤이 들어가는 컬럼은 너비를 명시한다
543
+
544
+ 컬럼 폭은 `width` 로 **고정**되고, `<td>` 는 그 폭을 넘는 내용을 잘라낸다(`overflow: hidden`). 텍스트라면 말줄임으로 끝나지만, 셀에 `STag` · `SButton` · `SGhostButton` · `SSelect` · `SInput` · `SNumberInput` 처럼 **자기 폭을 가진 요소**를 넣으면 요소 자체가 잘려 **누르거나 읽거나 입력할 수 없게 된다.** `width` 를 생략해도 내용에 맞춰 늘어나지 않고 `STable` 의 기본 폭이 될 뿐이므로, 컨트롤이 들어가는 컬럼은 폭을 직접 판단해서 준다.
545
+
546
+ - 기준은 **요소가 온전히 보이는 폭 + 셀 좌우 패딩**이다. 좌우 패딩은 `STable` 이 토큰으로 넣으므로(직접 주지 않는다) 그만큼을 뺀 나머지가 요소 몫이라는 점을 계산에 넣는다.
547
+ - 요소가 둘 이상이면 요소 폭의 합에 **`gap` 까지** 더한다 (행 내부 인라인 액션 간격은 `gap-sd-4` 고정, §3-5-5).
548
+ - 값에 따라 폭이 달라지는 요소(`STag` 라벨, 라벨 있는 버튼)는 **가장 긴 값** 기준으로 잡는다. `판매중` 에 맞춰두면 `판매중지 요청` 에서 잘린다.
549
+ - `SSelect` · `SInput` 처럼 셀 폭을 채우는 컨트롤은 **컬럼 폭이 곧 컨트롤 폭**이다. 실제 선택값·입력값이 말줄임 없이 읽히는 폭인지 확인한다.
550
+ - 폭을 넉넉히 줄 수 없는 자리는 폭을 줄이는 게 아니라 **요소를 바꾼다** — 라벨 버튼 대신 아이콘만 있는 `SGhostButton`, `size="xs"` (§3-5-2, §3-5-5).
551
+ - **`autoWidth` 는 해법이 아니다.** 내용에 맞춰 늘어나는 게 아니라 고정폭 컬럼들이 가져가고 **남은 폭을 나눠 갖는 것**이라, 테이블이 좁으면 역시 잘린다. 컨트롤 컬럼은 `width` 로 직접 확보한다.
552
+
553
+ **`resizable` 테이블이면 `minWidth` 를 함께 준다.** resize 하한 기본값은 어떤 컨트롤도 담지 못할 만큼 작아, 사용자가 끝까지 끌면 그대로 잘린다. `width` 를 정한 근거와 같은 값을 하한으로 둔다 — 텍스트 컬럼과 달리 여기서는 더 줄일 여지가 없다.
554
+
555
+ ```tsx
556
+ const columns: STableColumn[] = [
557
+ // 태그 — 가장 긴 라벨 기준
558
+ { name: 'status', label: '상태', field: 'status', width: '120px', minWidth: 120, align: 'center',
559
+ render: (row: SRow) => <STag size="sm" color="green" label={row.statusLabel} /> },
560
+ // 셀 안 입력 — 컬럼 폭이 곧 입력 폭
561
+ { name: 'qty', label: '수량', field: 'qty', width: '100px', minWidth: 100, align: 'right',
562
+ render: (row: SRow) => <SNumberInput value={row.qty} onValueChange={v => setQty(row, v)} /> },
563
+ // 인라인 액션 둘 — 폭 = xs 버튼 2개 + gap-sd-4 + 셀 좌우 패딩
564
+ { name: 'actions', label: '', field: 'id', width: '84px', minWidth: 84, align: 'center',
565
+ render: (row: SRow) => (
566
+ <div className="flex items-center justify-center gap-sd-4">
567
+ <SGhostButton size="xs" intent="action" icon="edit" ariaLabel="수정" onClick={() => editRow(row)} />
568
+ <SGhostButton size="xs" icon="delete" ariaLabel="삭제" onClick={() => confirmRemove(row)} />
569
+ </div>
570
+ ) },
571
+
572
+ // ❌ 컨트롤 컬럼에 width 생략 — 기본 폭에 맡기면 버튼이 잘린다
573
+ { name: 'move', label: '', field: 'id', render: () => <SButton label="재고 이동" size="xs" /> },
574
+ ];
575
+ ```
576
+
540
577
  #### 값이 없는 셀은 회색 하이픈
541
578
 
542
579
  셀을 **빈칸으로 두지 않는다.** 값이 `null` · `undefined` · 빈 문자열이면 `-` 를 `text-fg-tertiary`(`grey_65`)로 표시한다.
@@ -1020,7 +1057,7 @@ export default function ProductDetailPage() {
1020
1057
  - [ ] `SPage`·`SPopup` 의 기본 패딩을 `p-sd-*` 로 덮어쓰지 않았는가, 블록·섹션 **간격**이 `gap-sd-12` 인가 (`gap-sd-16`/`gap-sd-24` ❌ — 24 는 안쪽 여백에만 열린다)
1021
1058
  - [ ] 섹션·패널의 안쪽 **여백**이 §2-2 판정과 맞는가 (덩어리 두 종류 → 16 / 세 종류 이상 → 24, 서면 16)
1022
1059
  - [ ] `SSectionHeaderCard.Body` 의 여백을 `p-sd-*` 가 아니라 `padding` prop 으로 줬는가
1023
- - [ ] 자체 스크롤하는 패널의 하단에 `pb-sd-80` 이 있는가, 페이지네이션 있는 목록에서 `scrollEndSpacing={false}` 를 넘겼는가
1060
+ - [ ] 자체 스크롤하는 패널의 하단에 `pb-[var(--cmp-pageBody-padding-scrollEnd)]` 이 있는가, 페이지네이션 있는 목록에서 `scrollEndSpacing={false}` 를 넘겼는가
1024
1061
  - [ ] 같은 컴포넌트를 나열할 때 §2-2 그룹 간격을 썼는가 (체크박스 가로 `gap-sd-24` 등)
1025
1062
  - [ ] 페이지가 §4의 표준 골격에서 시작했는가
1026
1063
  - [ ] 앱 셸이나 그 바깥에 `min-width`·`overflow-x` 를 직접 걸지 않았는가 (최소 너비는 `SLayout` 이 보장한다, §4-1)
@@ -1030,6 +1067,7 @@ export default function ProductDetailPage() {
1030
1067
  - [ ] 상태 표시에 `STag size="sm"` 을 썼는가
1031
1068
  - [ ] 테이블에서 양을 나타내는 컬럼(금액·수량·개수 등)이 전부 `align: 'right'` 인가
1032
1069
  - [ ] 번호·코드·전화번호·일자 컬럼에 `align: 'center'` 를 **명시**했는가 (생략하면 좌측이 된다)
1070
+ - [ ] 컨트롤(`STag`·`SButton`·`SSelect`·`SInput` …)이 들어가는 컬럼에 `width` 를 명시했는가, `resizable` 이면 `minWidth` 도 줬는가 (§3-4 — 폭이 모자라면 요소가 잘려 못 쓴다)
1033
1071
  - [ ] 금액·수량 등 양을 나타내는 숫자에 빠짐없이 `toLocaleString()` 을 썼는가 (번호·코드는 제외)
1034
1072
  - [ ] 하단 버튼이 양끝 분리(`justify-between`)이고, 되돌리기가 왼쪽 끝 · 실행이 오른쪽 끝인가
1035
1073
  - [ ] 페이지에 `color="primary"` 채움 버튼이 **1개뿐**인가 (`danger` 채움도 1개, `SDropdownButton` 포함)
@@ -3267,7 +3305,7 @@ function OrderModal({ open, onOpenChange, onClose, modalRef, orderId }: OrderMod
3267
3305
  |------|------|---------|-------------|
3268
3306
  | `popupTitle?` | `string` | `''` | 헤더 제목 |
3269
3307
  | `type?` | `SPopupType` | `'default'` | 타입 (헤더 색상) |
3270
- | `useFooter?` | `boolean` | `true` | 하단 footer 표시 여부 |
3308
+ | `useFooter?` | `boolean` | `false` | 하단 footer 표시 여부 |
3271
3309
  | `submitButton?` | `SPopupSubmitButton` | — | 확인 버튼 설정 |
3272
3310
  | `footerLeft?` | `ReactNode` | — | footer 좌측 영역 |
3273
3311
  | `noPadding?` | `boolean` | `false` | 본문 기본 패딩을 없앱니다. 표를 가장자리까지 채우는 등 본문이 직접 여백을 다룰 때만 사용합니다. |
package/dist/llms.txt CHANGED
@@ -270,7 +270,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
270
270
 
271
271
  | 스크롤 종류 | 어떻게 |
272
272
  | --- | --- |
273
- | **패널 자체 스크롤** (좌측 목록, 중앙 대화 등) | 그 패널 안쪽 하단에 `pb-sd-80` |
273
+ | **패널 자체 스크롤** (좌측 목록, 중앙 대화 등) | 그 패널 안쪽 하단에 `pb-[var(--cmp-pageBody-padding-scrollEnd)]` — `SPage` 와 같은 토큰이라 값이 바뀌어도 함께 따라간다 |
274
274
  | **페이지 단위 스크롤** | **`SPage` 가 넣는다. 직접 주지 않는다** |
275
275
 
276
276
  `SPage` 는 기본으로 넣으므로 **아무것도 하지 않으면 맞다.** 끄는 경우는 하나뿐이다 — **페이지네이션이 붙은 테이블.** 페이지네이션이 이미 "여기서 끝"을 알려주므로 `scrollEndSpacing={false}` 로 끈다 (§4-2 목록 페이지).
@@ -414,7 +414,7 @@ Tailwind 유틸리티는 **토큰 스케일에 있는 값만** 사용한다.
414
414
 
415
415
  - 작은 창이 아니라 **화면 하나가 통째로** 들어간다 — 검색 필터·테이블·페이지네이션이 그대로 있는 목록(`엑셀 파일 관리`), 헤더 카드·탭이 있는 상세(`이동 오더 상세`).
416
416
  - 상세를 팝업으로 여는 이유는 **목록을 떠나지 않고 여러 건을 번갈아 보기** 위해서다. 단 상세가 **항상** 팝업인 것은 아니고, 데이터 양이 많을 때 팝업을 쓴다.
417
- - 구조는 헤더(제목 중앙) + 본문 + 푸터다. **확정할 작업이 있으면 푸터에 `저장`, 조회만이면 `useFooter={false}`** 로 푸터를 없앤다.
417
+ - 구조는 헤더(제목 중앙) + 본문이고 **푸터는 기본으로 없다**. 조회만 하는 팝업은 그대로 두고, **확정할 작업이 있을 때만 `useFooter` 로 푸터를 켜서 `submitButton` 에 `저장`** 을 둔다.
418
418
  - **본문 패딩은 `SPopup` 이 토큰으로 넣는다. 직접 주지 않는다** (`p-sd-*` 로 덮어쓰면 토큰이 바뀌어도 안 따라간다). 표를 가장자리까지 채우는 등 콘텐츠가 여백을 직접 다뤄야 할 때만 `noPadding` 으로 끈다.
419
419
  - 그 밖에는 팝업 안도 일반 페이지와 같은 규칙(§4)을 따른다: 블록 간격 `gap-sd-12`, 표는 `SKeyValueTable` / `STable`.
420
420
 
@@ -431,7 +431,7 @@ function openDetailPopup(orderId: string) {
431
431
  // 2) 그 라우트의 루트에 SPopup 을 둔다 (조회만 → 푸터 없음)
432
432
  export default function TransferOrderPopupPage() {
433
433
  return (
434
- <SPopup popupTitle="이동 오더 상세" useFooter={false}>
434
+ <SPopup popupTitle="이동 오더 상세">
435
435
  {/* 본문 패딩은 SPopup 이 토큰으로 넣는다 — p-sd-* 로 덮어쓰지 않는다 */}
436
436
  <div className="flex flex-col gap-sd-12">
437
437
  <SSectionHeaderCard>…</SSectionHeaderCard>
@@ -494,7 +494,9 @@ SModal.create({ component: OrderModal, componentProps: { orderId } })
494
494
 
495
495
  작성 중인 내용이 있을 때 닫기를 시도하면 이탈 안내를 띄우는 것은 **소비 앱 몫**이다. 디자인 시스템은 dirty 상태를 알지 못하므로 백드롭·ESC 를 일괄 차단할 뿐이다. 안내가 필요하면 앱이 자체 dirty 판정 후 `SModal.confirm` 으로 띄운다.
496
496
 
497
- ### 3-4. 테이블 컬럼 정렬
497
+ ### 3-4. 테이블 컬럼 — 정렬과 너비
498
+
499
+ #### 정렬
498
500
 
499
501
  **값의 크기를 비교하는 숫자 컬럼은 예외 없이 오른쪽 정렬한다** (`align: 'right'`).
500
502
  자릿수가 세로로 맞아야 값의 크기를 눈으로 비교할 수 있기 때문이다.
@@ -538,6 +540,41 @@ const columns: STableColumn[] = [
538
540
 
539
541
  - `SKeyValueTable` 의 값 셀도 같은 기준을 따른다.
540
542
 
543
+ #### 컨트롤이 들어가는 컬럼은 너비를 명시한다
544
+
545
+ 컬럼 폭은 `width` 로 **고정**되고, `<td>` 는 그 폭을 넘는 내용을 잘라낸다(`overflow: hidden`). 텍스트라면 말줄임으로 끝나지만, 셀에 `STag` · `SButton` · `SGhostButton` · `SSelect` · `SInput` · `SNumberInput` 처럼 **자기 폭을 가진 요소**를 넣으면 요소 자체가 잘려 **누르거나 읽거나 입력할 수 없게 된다.** `width` 를 생략해도 내용에 맞춰 늘어나지 않고 `STable` 의 기본 폭이 될 뿐이므로, 컨트롤이 들어가는 컬럼은 폭을 직접 판단해서 준다.
546
+
547
+ - 기준은 **요소가 온전히 보이는 폭 + 셀 좌우 패딩**이다. 좌우 패딩은 `STable` 이 토큰으로 넣으므로(직접 주지 않는다) 그만큼을 뺀 나머지가 요소 몫이라는 점을 계산에 넣는다.
548
+ - 요소가 둘 이상이면 요소 폭의 합에 **`gap` 까지** 더한다 (행 내부 인라인 액션 간격은 `gap-sd-4` 고정, §3-5-5).
549
+ - 값에 따라 폭이 달라지는 요소(`STag` 라벨, 라벨 있는 버튼)는 **가장 긴 값** 기준으로 잡는다. `판매중` 에 맞춰두면 `판매중지 요청` 에서 잘린다.
550
+ - `SSelect` · `SInput` 처럼 셀 폭을 채우는 컨트롤은 **컬럼 폭이 곧 컨트롤 폭**이다. 실제 선택값·입력값이 말줄임 없이 읽히는 폭인지 확인한다.
551
+ - 폭을 넉넉히 줄 수 없는 자리는 폭을 줄이는 게 아니라 **요소를 바꾼다** — 라벨 버튼 대신 아이콘만 있는 `SGhostButton`, `size="xs"` (§3-5-2, §3-5-5).
552
+ - **`autoWidth` 는 해법이 아니다.** 내용에 맞춰 늘어나는 게 아니라 고정폭 컬럼들이 가져가고 **남은 폭을 나눠 갖는 것**이라, 테이블이 좁으면 역시 잘린다. 컨트롤 컬럼은 `width` 로 직접 확보한다.
553
+
554
+ **`resizable` 테이블이면 `minWidth` 를 함께 준다.** resize 하한 기본값은 어떤 컨트롤도 담지 못할 만큼 작아, 사용자가 끝까지 끌면 그대로 잘린다. `width` 를 정한 근거와 같은 값을 하한으로 둔다 — 텍스트 컬럼과 달리 여기서는 더 줄일 여지가 없다.
555
+
556
+ ```tsx
557
+ const columns: STableColumn[] = [
558
+ // 태그 — 가장 긴 라벨 기준
559
+ { name: 'status', label: '상태', field: 'status', width: '120px', minWidth: 120, align: 'center',
560
+ render: (row: SRow) => <STag size="sm" color="green" label={row.statusLabel} /> },
561
+ // 셀 안 입력 — 컬럼 폭이 곧 입력 폭
562
+ { name: 'qty', label: '수량', field: 'qty', width: '100px', minWidth: 100, align: 'right',
563
+ render: (row: SRow) => <SNumberInput value={row.qty} onValueChange={v => setQty(row, v)} /> },
564
+ // 인라인 액션 둘 — 폭 = xs 버튼 2개 + gap-sd-4 + 셀 좌우 패딩
565
+ { name: 'actions', label: '', field: 'id', width: '84px', minWidth: 84, align: 'center',
566
+ render: (row: SRow) => (
567
+ <div className="flex items-center justify-center gap-sd-4">
568
+ <SGhostButton size="xs" intent="action" icon="edit" ariaLabel="수정" onClick={() => editRow(row)} />
569
+ <SGhostButton size="xs" icon="delete" ariaLabel="삭제" onClick={() => confirmRemove(row)} />
570
+ </div>
571
+ ) },
572
+
573
+ // ❌ 컨트롤 컬럼에 width 생략 — 기본 폭에 맡기면 버튼이 잘린다
574
+ { name: 'move', label: '', field: 'id', render: () => <SButton label="재고 이동" size="xs" /> },
575
+ ];
576
+ ```
577
+
541
578
  #### 값이 없는 셀은 회색 하이픈
542
579
 
543
580
  셀을 **빈칸으로 두지 않는다.** 값이 `null` · `undefined` · 빈 문자열이면 `-` 를 `text-fg-tertiary`(`grey_65`)로 표시한다.
@@ -1021,7 +1058,7 @@ export default function ProductDetailPage() {
1021
1058
  - [ ] `SPage`·`SPopup` 의 기본 패딩을 `p-sd-*` 로 덮어쓰지 않았는가, 블록·섹션 **간격**이 `gap-sd-12` 인가 (`gap-sd-16`/`gap-sd-24` ❌ — 24 는 안쪽 여백에만 열린다)
1022
1059
  - [ ] 섹션·패널의 안쪽 **여백**이 §2-2 판정과 맞는가 (덩어리 두 종류 → 16 / 세 종류 이상 → 24, 서면 16)
1023
1060
  - [ ] `SSectionHeaderCard.Body` 의 여백을 `p-sd-*` 가 아니라 `padding` prop 으로 줬는가
1024
- - [ ] 자체 스크롤하는 패널의 하단에 `pb-sd-80` 이 있는가, 페이지네이션 있는 목록에서 `scrollEndSpacing={false}` 를 넘겼는가
1061
+ - [ ] 자체 스크롤하는 패널의 하단에 `pb-[var(--cmp-pageBody-padding-scrollEnd)]` 이 있는가, 페이지네이션 있는 목록에서 `scrollEndSpacing={false}` 를 넘겼는가
1025
1062
  - [ ] 같은 컴포넌트를 나열할 때 §2-2 그룹 간격을 썼는가 (체크박스 가로 `gap-sd-24` 등)
1026
1063
  - [ ] 페이지가 §4의 표준 골격에서 시작했는가
1027
1064
  - [ ] 앱 셸이나 그 바깥에 `min-width`·`overflow-x` 를 직접 걸지 않았는가 (최소 너비는 `SLayout` 이 보장한다, §4-1)
@@ -1031,6 +1068,7 @@ export default function ProductDetailPage() {
1031
1068
  - [ ] 상태 표시에 `STag size="sm"` 을 썼는가
1032
1069
  - [ ] 테이블에서 양을 나타내는 컬럼(금액·수량·개수 등)이 전부 `align: 'right'` 인가
1033
1070
  - [ ] 번호·코드·전화번호·일자 컬럼에 `align: 'center'` 를 **명시**했는가 (생략하면 좌측이 된다)
1071
+ - [ ] 컨트롤(`STag`·`SButton`·`SSelect`·`SInput` …)이 들어가는 컬럼에 `width` 를 명시했는가, `resizable` 이면 `minWidth` 도 줬는가 (§3-4 — 폭이 모자라면 요소가 잘려 못 쓴다)
1034
1072
  - [ ] 금액·수량 등 양을 나타내는 숫자에 빠짐없이 `toLocaleString()` 을 썼는가 (번호·코드는 제외)
1035
1073
  - [ ] 하단 버튼이 양끝 분리(`justify-between`)이고, 되돌리기가 왼쪽 끝 · 실행이 오른쪽 끝인가
1036
1074
  - [ ] 페이지에 `color="primary"` 채움 버튼이 **1개뿐**인가 (`danger` 채움도 1개, `SDropdownButton` 포함)
package/dist/styles.css CHANGED
@@ -703,6 +703,9 @@
703
703
  .z-\[9000\] {
704
704
  z-index: 9000;
705
705
  }
706
+ .z-auto {
707
+ z-index: auto;
708
+ }
706
709
  .col-start-1 {
707
710
  grid-column-start: 1;
708
711
  }
@@ -2533,12 +2536,12 @@
2533
2536
  .pb-\[var\(--cmp-modal-action-body-paddingBottom\)\] {
2534
2537
  padding-bottom: var(--cmp-modal-action-body-paddingBottom);
2535
2538
  }
2539
+ .pb-\[var\(--cmp-pageBody-padding-scrollEnd\)\] {
2540
+ padding-bottom: var(--cmp-pageBody-padding-scrollEnd);
2541
+ }
2536
2542
  .pb-sd-16 {
2537
2543
  padding-bottom: var(--spacing-sd-16);
2538
2544
  }
2539
- .pb-sd-80 {
2540
- padding-bottom: var(--spacing-sd-80);
2541
- }
2542
2545
  .pl-\(--cmp-guide-contents-depth-paddingLeft\) {
2543
2546
  padding-left: var(--cmp-guide-contents-depth-paddingLeft);
2544
2547
  }
@@ -5145,6 +5148,7 @@
5145
5148
  --cmp-listDraggableItem-accentStripe-color: #64ABFF;
5146
5149
  --cmp-listDraggableItem-dragging-content-opacity: 60%;
5147
5150
  --cmp-pageBody-padding-default: 16px;
5151
+ --cmp-pageBody-padding-scrollEnd: 80px;
5148
5152
  --cmp-sectionHeaderCard-radius: 8px;
5149
5153
  --cmp-sectionHeaderCard-border: #E1E1E1;
5150
5154
  --cmp-sectionHeaderCard-topBorder-thickness: 2px;
package/dist/theme.css CHANGED
@@ -1350,6 +1350,7 @@
1350
1350
  --cmp-listDraggableItem-accentStripe-color: #64ABFF;
1351
1351
  --cmp-listDraggableItem-dragging-content-opacity: 60%;
1352
1352
  --cmp-pageBody-padding-default: 16px;
1353
+ --cmp-pageBody-padding-scrollEnd: 80px;
1353
1354
  --cmp-sectionHeaderCard-radius: 8px;
1354
1355
  --cmp-sectionHeaderCard-border: #E1E1E1;
1355
1356
  --cmp-sectionHeaderCard-topBorder-thickness: 2px;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sellmate-design-system-react",
3
- "version": "6.1.0",
3
+ "version": "7.0.0",
4
4
  "description": "Sellmate Design System — React (TypeScript + Tailwind v4) port",
5
5
  "license": "UNLICENSED",
6
6
  "keywords": [