sh-ui-cli 0.83.0 → 0.85.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.
@@ -2,6 +2,32 @@
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "$description": "sh-ui 릴리즈 노트 단일 소스. docs(React)와 showcase(Flutter)가 함께 읽는다. 새 릴리즈마다 맨 앞에 추가.",
4
4
  "versions": [
5
+ {
6
+ "version": "0.85.0",
7
+ "date": "2026-05-13",
8
+ "title": "Sidebar floating variant — --sidebar-floating-padding 토큰 + icon collapsed 폭 자동 보정",
9
+ "type": "minor",
10
+ "highlights": [
11
+ "**`--sidebar-floating-padding` 토큰 신설 (DX 3)** — floating variant 의 outer 카드 가장자리 spacing 이 `var(--space-2)` 하드코딩이라 사용자가 키우려고 outer padding override 시 inner 카드 height calc 가 추종 안 돼 하단이 잘리던 결함. outer padding · inner height(`100svh - 2 * floating-padding`) · inner top 모두 같은 변수 참조. `<SidebarProvider style={{ ['--sidebar-floating-padding']: '0.75rem' }}>` 으로 한 번에 조정.",
12
+ "**floating + icon collapsed 폭 자동 보정 (DX 2)** — outer aside 48px 에서 floating padding 16px 빼면 inner 카드가 32px 로 옹색해지던 결함. `[data-variant=\"floating\"][data-collapsible=\"icon\"]` 조합 시 outer width = `--sidebar-width-icon + 2 * --sidebar-floating-padding` 으로 자동 보정. inner usable 폭이 non-floating 의 48px 와 동일. 사용자가 매번 `--sidebar-width-icon` 을 inline style 로 보정하던 atlas 워크어라운드 일소.",
13
+ "**v0.82.2 changelog 정정** — 이전 \"알려진 이슈\" 로 기록한 `@base-ui/react@1.4.1` id mismatch 는 Base UI 결함이 **아니었음**. atlas 측 `zustand + persist` 미들웨어 (`useSyncExternalStore` 통합) 가 Next.js 16 streaming SSR 의 트리 일관성을 어긋나게 만들어 React `useId()` 가 인코딩하는 트리 path 가 server/client 에서 달라진 것. atlas 가 zustand 를 React Query (server state) + cookie-backed hook (client state) 로 대체하니 모든 hydration mismatch 즉시 해소. Base UI 1.4.1 그대로 사용 안전. v0.82.2 entry 의 알려진 이슈 줄 제거.",
14
+ "3 변종 모두 적용 — plain `styles.css` / module `styles.module.css` 의 floating 블록 + tailwind `sidebarRoot` 의 padding · innerWrap height/top. 모두 토큰의 fallback `var(--space-2)` 를 그대로 유지해 기존 동작 동일."
15
+ ],
16
+ "url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.85.0"
17
+ },
18
+ {
19
+ "version": "0.84.0",
20
+ "date": "2026-05-13",
21
+ "title": "Sidebar — Brand 컴파운드 primitive (브랜드 카드 · 워크스페이스 스위처 · 유저 프로필)",
22
+ "type": "minor",
23
+ "highlights": [
24
+ "**`SidebarBrand` 컴파운드 primitive 신설 — 브랜드 카드 / 워크스페이스 스위처 / 유저 프로필 의 90% 를 커버** — `<SidebarHeader>` / `<SidebarFooter>` 안에서 매번 박던 패턴 (icon + title + subtitle + trailing chevron) 을 단일 합성으로. Card/Dialog 와 동일한 separate-exports 컨벤션.",
25
+ "**Exports (6개)**: `SidebarBrand` (행 wrapper, 내부 `data-when-collapsed=\"center\"` 자동 적용) · `SidebarBrandIcon` (항상 visible, collapsed 시 sole 요소) · `SidebarBrandText` (`data-when-collapsed=\"hide\"`) · `SidebarBrandTitle` (단일 줄 본문, truncate 내장) · `SidebarBrandSubtitle` (보조 줄, foreground-muted) · `SidebarBrandAction` (`data-when-collapsed=\"hide\"`, ml-auto trailing icon).",
26
+ "**v0.83.0 의 `data-when-collapsed` 컨벤션 내장** — 사용자가 attribute 박지 않아도 collapsible=\"icon\" 모드 전환 시 자동 적응. 인터랙티브 케이스는 `<DropdownMenuTrigger render={<button data-when-collapsed=\"strip-chrome\" />}>` 로 wrap.",
27
+ "**3 변종 모두 지원** — plain `styles.css` / module `styles.module.css` 에 룰 직접, tailwind 변종은 utility 클래스로 동등 스타일. 듀얼카피 (apps/docs) 동기화."
28
+ ],
29
+ "url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.84.0"
30
+ },
5
31
  {
6
32
  "version": "0.83.0",
7
33
  "date": "2026-05-13",
@@ -30,8 +56,7 @@
30
56
  "title": "RootLayout themeInitScript 가 'system' 케이스 누락하던 FOUC 결함 수정",
31
57
  "type": "patch",
32
58
  "highlights": [
33
- "**RootLayout 의 FOUC 차단 inline script 가 `theme === 'system'` 케이스를 처리 안 해 새로고침 시 light → dark 깜빡임이 남던 결함 수정** — 기존 스크립트는 `t === 'dark'` 또는 `!t && system pref dark` 만 처리, `t === 'system'` 일 땐 next-themes 가 resolve 한 후에야 `.dark` 가 박혀 한 frame 흰 깜빡임. next-themes 의 `setTheme('system')` 호출 직후 localStorage 에 `'system'` 문자열이 들어가는데 이 케이스가 누락. matrix 정정: `'dark'` → `.dark`, `'light'` → none, `'system'`/unset → system pref 따라감. 6개 RootLayout 템플릿 (nextjs-app · nextjs-standalone × flat/mes/fsd) 일괄 수정.",
34
- "**알려진 이슈 (이번 릴리즈 미반영)** — `@base-ui/react` 1.4.1 의 id 생성기가 Dialog/DropdownMenu/Popover Trigger 에서 server/client id 불일치로 hydration mismatch console error 발생. upstream 이슈 — 1.4.1 이 npm 최신이라 dep bump 로 해결 불가. 기능 영향은 없고 console error 만 뜸. 향후 Base UI 픽스 버전 나오면 peerDep 갱신."
59
+ "**RootLayout 의 FOUC 차단 inline script 가 `theme === 'system'` 케이스를 처리 안 해 새로고침 시 light → dark 깜빡임이 남던 결함 수정** — 기존 스크립트는 `t === 'dark'` 또는 `!t && system pref dark` 만 처리, `t === 'system'` 일 땐 next-themes 가 resolve 한 후에야 `.dark` 가 박혀 한 frame 흰 깜빡임. next-themes 의 `setTheme('system')` 호출 직후 localStorage 에 `'system'` 문자열이 들어가는데 이 케이스가 누락. matrix 정정: `'dark'` → `.dark`, `'light'` → none, `'system'`/unset → system pref 따라감. 6개 RootLayout 템플릿 (nextjs-app · nextjs-standalone × flat/mes/fsd) 일괄 수정."
35
60
  ],
36
61
  "url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.82.2"
37
62
  },
@@ -511,6 +511,66 @@ export function SidebarFooter({ className, ...props }: React.HTMLAttributes<HTML
511
511
  );
512
512
  }
513
513
 
514
+ /* ───────────── Brand (compound primitive, v0.84.0+) ─────────────
515
+ * data-when-collapsed 컨벤션 내장. plain 변종 주석 참고. */
516
+
517
+ export function SidebarBrand({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
518
+ return (
519
+ <div
520
+ data-when-collapsed="center"
521
+ className={cn(styles.sidebar__brand, className)}
522
+ {...props}
523
+ />
524
+ );
525
+ }
526
+
527
+ export function SidebarBrandIcon({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
528
+ return (
529
+ <div
530
+ className={cn(styles.sidebar__brand_icon, className)}
531
+ {...props}
532
+ />
533
+ );
534
+ }
535
+
536
+ export function SidebarBrandText({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
537
+ return (
538
+ <div
539
+ data-when-collapsed="hide"
540
+ className={cn(styles.sidebar__brand_text, className)}
541
+ {...props}
542
+ />
543
+ );
544
+ }
545
+
546
+ export function SidebarBrandTitle({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
547
+ return (
548
+ <div
549
+ className={cn(styles.sidebar__brand_title, className)}
550
+ {...props}
551
+ />
552
+ );
553
+ }
554
+
555
+ export function SidebarBrandSubtitle({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
556
+ return (
557
+ <div
558
+ className={cn(styles.sidebar__brand_subtitle, className)}
559
+ {...props}
560
+ />
561
+ );
562
+ }
563
+
564
+ export function SidebarBrandAction({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
565
+ return (
566
+ <div
567
+ data-when-collapsed="hide"
568
+ className={cn(styles.sidebar__brand_action, className)}
569
+ {...props}
570
+ />
571
+ );
572
+ }
573
+
514
574
  /** Sidebar의 스크롤 영역. 메뉴/그룹 목록을 둔다. */
515
575
  export function SidebarContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
516
576
  return (
@@ -172,8 +172,10 @@ export interface SidebarProps extends React.HTMLAttributes<HTMLDivElement> {
172
172
  collapsible?: "offcanvas" | "icon" | "none";
173
173
  }
174
174
 
175
+ // v0.85.0+ — floating variant 의 outer padding + inner h/top 이 모두 --sidebar-floating-padding
176
+ // 토큰 (default var(--space-2)) 을 추종. DX 2: floating+icon collapsed 시 outer width 자동 보정.
175
177
  const sidebarRoot =
176
- "flex flex-col w-[var(--sidebar-width)] shrink-0 bg-[var(--sidebar-bg)] text-[var(--sidebar-fg)] border-r border-[var(--sidebar-border)] transition-[width] duration-[var(--duration-slow)] relative z-[5] data-[side=right]:border-r-0 data-[side=right]:border-l data-[side=right]:order-1 data-[state=collapsed]:data-[collapsible=offcanvas]:w-0 data-[state=collapsed]:data-[collapsible=offcanvas]:border-r-0 data-[state=collapsed]:data-[collapsible=offcanvas]:border-l-0 data-[state=collapsed]:data-[collapsible=offcanvas]:overflow-hidden data-[state=collapsed]:data-[collapsible=icon]:w-[var(--sidebar-width-icon)] data-[variant=floating]:border-none data-[variant=floating]:p-[var(--space-2)] data-[variant=floating]:bg-transparent data-[variant=inset]:bg-transparent data-[variant=inset]:border-none motion-reduce:transition-none";
178
+ "flex flex-col w-[var(--sidebar-width)] shrink-0 bg-[var(--sidebar-bg)] text-[var(--sidebar-fg)] border-r border-[var(--sidebar-border)] transition-[width] duration-[var(--duration-slow)] relative z-[5] data-[side=right]:border-r-0 data-[side=right]:border-l data-[side=right]:order-1 data-[state=collapsed]:data-[collapsible=offcanvas]:w-0 data-[state=collapsed]:data-[collapsible=offcanvas]:border-r-0 data-[state=collapsed]:data-[collapsible=offcanvas]:border-l-0 data-[state=collapsed]:data-[collapsible=offcanvas]:overflow-hidden data-[state=collapsed]:data-[collapsible=icon]:w-[var(--sidebar-width-icon)] data-[state=collapsed]:data-[collapsible=icon]:data-[variant=floating]:w-[calc(var(--sidebar-width-icon)+2*var(--sidebar-floating-padding,var(--space-2)))] data-[variant=floating]:border-none data-[variant=floating]:p-[var(--sidebar-floating-padding,var(--space-2))] data-[variant=floating]:bg-transparent data-[variant=inset]:bg-transparent data-[variant=inset]:border-none motion-reduce:transition-none";
177
179
 
178
180
  export function Sidebar({ side = "left", variant = "sidebar", collapsible = "offcanvas", className, children, ...props }: SidebarProps) {
179
181
  const { isMobile, state, openMobile, setOpenMobile } = useSidebar();
@@ -198,7 +200,7 @@ export function Sidebar({ side = "left", variant = "sidebar", collapsible = "off
198
200
 
199
201
  const innerWrap =
200
202
  variant === "floating"
201
- ? "flex flex-col h-[calc(100svh-1rem)] sticky top-[var(--space-2)] overflow-hidden border border-[var(--sidebar-border)] rounded-[var(--radius)] bg-[var(--sidebar-bg)]"
203
+ ? "flex flex-col h-[calc(100svh-2*var(--sidebar-floating-padding,var(--space-2)))] sticky top-[var(--sidebar-floating-padding,var(--space-2))] overflow-hidden border border-[var(--sidebar-border)] rounded-[var(--radius)] bg-[var(--sidebar-bg)]"
202
204
  : "flex flex-col h-[100svh] sticky top-0 overflow-hidden";
203
205
 
204
206
  return wrap(
@@ -352,6 +354,66 @@ export function SidebarFooter({ className, ...props }: React.HTMLAttributes<HTML
352
354
  return <div className={cn("flex flex-col gap-[var(--space-2)] p-[var(--space-2)] overflow-hidden", className)} {...props} />;
353
355
  }
354
356
 
357
+ /* ───────────── Brand (compound primitive, v0.84.0+) ─────────────
358
+ * data-when-collapsed 컨벤션 내장. plain 변종 주석 참고. */
359
+
360
+ export function SidebarBrand({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
361
+ return (
362
+ <div
363
+ data-when-collapsed="center"
364
+ className={cn("flex flex-row items-center gap-[var(--space-2)] px-[var(--space-2)] py-[var(--space-2)] rounded-[var(--radius)] min-w-0", className)}
365
+ {...props}
366
+ />
367
+ );
368
+ }
369
+
370
+ export function SidebarBrandIcon({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
371
+ return (
372
+ <div
373
+ className={cn("flex items-center justify-center shrink-0 size-8 rounded-[calc(var(--radius)-2px)]", className)}
374
+ {...props}
375
+ />
376
+ );
377
+ }
378
+
379
+ export function SidebarBrandText({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
380
+ return (
381
+ <div
382
+ data-when-collapsed="hide"
383
+ className={cn("flex flex-col flex-1 min-w-0 gap-0", className)}
384
+ {...props}
385
+ />
386
+ );
387
+ }
388
+
389
+ export function SidebarBrandTitle({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
390
+ return (
391
+ <div
392
+ className={cn("text-[length:var(--text-sm)] font-[var(--weight-medium)] leading-tight truncate text-[color:var(--sidebar-fg)]", className)}
393
+ {...props}
394
+ />
395
+ );
396
+ }
397
+
398
+ export function SidebarBrandSubtitle({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
399
+ return (
400
+ <div
401
+ className={cn("text-[length:var(--text-xs)] leading-tight truncate text-[color:var(--foreground-muted)]", className)}
402
+ {...props}
403
+ />
404
+ );
405
+ }
406
+
407
+ export function SidebarBrandAction({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
408
+ return (
409
+ <div
410
+ data-when-collapsed="hide"
411
+ className={cn("ml-auto shrink-0 flex items-center justify-center", className)}
412
+ {...props}
413
+ />
414
+ );
415
+ }
416
+
355
417
  export function SidebarContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
356
418
  return <div className={cn("flex flex-col flex-1 min-h-0 overflow-y-auto gap-0", className)} {...props} />;
357
419
  }
@@ -521,6 +521,92 @@ export function SidebarFooter({ className, ...props }: React.HTMLAttributes<HTML
521
521
  );
522
522
  }
523
523
 
524
+ /* ───────────── Brand (compound primitive, v0.84.0+) ─────────────
525
+ * SidebarHeader / SidebarFooter 의 흔한 패턴 (브랜드 카드 · 워크스페이스 스위처 ·
526
+ * 유저 프로필) 의 90% 를 커버. data-when-collapsed 컨벤션을 내장해 사용자가
527
+ * attribute 박지 않아도 collapsed/icon 모드 자동 적응.
528
+ *
529
+ * 정적 예시:
530
+ * <SidebarBrand>
531
+ * <SidebarBrandIcon><Logo /></SidebarBrandIcon>
532
+ * <SidebarBrandText>
533
+ * <SidebarBrandTitle>Atlas</SidebarBrandTitle>
534
+ * <SidebarBrandSubtitle>workspace</SidebarBrandSubtitle>
535
+ * </SidebarBrandText>
536
+ * </SidebarBrand>
537
+ *
538
+ * 인터랙티브 (워크스페이스 스위처):
539
+ * <DropdownMenu>
540
+ * <DropdownMenuTrigger render={<button data-when-collapsed='strip-chrome' />}>
541
+ * <SidebarBrand>...</SidebarBrand>
542
+ * </DropdownMenuTrigger>
543
+ * ...
544
+ * </DropdownMenu>
545
+ */
546
+
547
+ /** 브랜드 행 wrapper. collapsed 일 땐 가운데 정렬 + 좌우 padding 0 (data-when-collapsed=center 내장). */
548
+ export function SidebarBrand({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
549
+ return (
550
+ <div
551
+ data-when-collapsed="center"
552
+ className={cn("sh-ui-sidebar__brand", className)}
553
+ {...props}
554
+ />
555
+ );
556
+ }
557
+
558
+ /** 항상 visible 한 아이콘 슬롯. collapsed 일 땐 SidebarBrand 의 유일한 요소. */
559
+ export function SidebarBrandIcon({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
560
+ return (
561
+ <div
562
+ className={cn("sh-ui-sidebar__brand-icon", className)}
563
+ {...props}
564
+ />
565
+ );
566
+ }
567
+
568
+ /** 텍스트 컨테이너. collapsed 일 땐 hidden (data-when-collapsed=hide 내장). */
569
+ export function SidebarBrandText({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
570
+ return (
571
+ <div
572
+ data-when-collapsed="hide"
573
+ className={cn("sh-ui-sidebar__brand-text", className)}
574
+ {...props}
575
+ />
576
+ );
577
+ }
578
+
579
+ /** 단일 줄 본문 (편의) — sh-ui-sidebar__brand-title 클래스만 박는다. */
580
+ export function SidebarBrandTitle({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
581
+ return (
582
+ <div
583
+ className={cn("sh-ui-sidebar__brand-title", className)}
584
+ {...props}
585
+ />
586
+ );
587
+ }
588
+
589
+ /** 보조 줄 — sh-ui-sidebar__brand-subtitle. text-foreground-muted + text-xs. */
590
+ export function SidebarBrandSubtitle({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
591
+ return (
592
+ <div
593
+ className={cn("sh-ui-sidebar__brand-subtitle", className)}
594
+ {...props}
595
+ />
596
+ );
597
+ }
598
+
599
+ /** Trailing icon (드롭다운 chevron 등). collapsed 일 땐 hidden. */
600
+ export function SidebarBrandAction({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
601
+ return (
602
+ <div
603
+ data-when-collapsed="hide"
604
+ className={cn("sh-ui-sidebar__brand-action", className)}
605
+ {...props}
606
+ />
607
+ );
608
+ }
609
+
524
610
  /** Sidebar의 스크롤 영역. 메뉴/그룹 목록을 둔다. */
525
611
  export function SidebarContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
526
612
  return (
@@ -83,18 +83,26 @@
83
83
  padding: var(--space-2);
84
84
  }
85
85
 
86
- /* variant: floating */
86
+ /* variant: floating
87
+ * --sidebar-floating-padding (v0.85.0+) — 카드 가장자리 spacing. SidebarProvider 에
88
+ * inline style 로 덮어쓰면 outer padding + inner height/top 자동 추종.
89
+ * 기본 var(--space-2). */
87
90
  .sh-ui-sidebar[data-variant="floating"] {
88
91
  border: none;
89
- padding: var(--space-2);
92
+ padding: var(--sidebar-floating-padding, var(--space-2));
90
93
  background: transparent;
91
94
  }
92
95
  .sh-ui-sidebar[data-variant="floating"] .sh-ui-sidebar__inner {
93
96
  border: 1px solid var(--sidebar-border);
94
97
  border-radius: var(--radius);
95
98
  background: var(--sidebar-bg);
96
- height: calc(100svh - 1rem);
97
- top: var(--space-2);
99
+ height: calc(100svh - 2 * var(--sidebar-floating-padding, var(--space-2)));
100
+ top: var(--sidebar-floating-padding, var(--space-2));
101
+ }
102
+ /* DX 2 (v0.85.0+) — floating + icon collapsed 조합 시 outer width 자동 보정.
103
+ * outer = icon + 2 * floating-padding 으로 키워 inner usable 폭을 non-floating 과 동일하게 유지. */
104
+ .sh-ui-sidebar[data-state="collapsed"][data-collapsible="icon"][data-variant="floating"] {
105
+ width: calc(var(--sidebar-width-icon) + 2 * var(--sidebar-floating-padding, var(--space-2)));
98
106
  }
99
107
 
100
108
  /* variant: inset (메인 영역이 카드 형태) */
@@ -318,6 +326,57 @@
318
326
  background-color: transparent;
319
327
  }
320
328
 
329
+ /* ───────────── Brand (compound primitive, v0.84.0+) ───────────── */
330
+ .sh-ui-sidebar__brand {
331
+ display: flex;
332
+ flex-direction: row;
333
+ align-items: center;
334
+ gap: var(--space-2);
335
+ padding: var(--space-2);
336
+ border-radius: var(--radius);
337
+ min-width: 0;
338
+ }
339
+ .sh-ui-sidebar__brand-icon {
340
+ display: flex;
341
+ align-items: center;
342
+ justify-content: center;
343
+ flex-shrink: 0;
344
+ width: 2rem;
345
+ height: 2rem;
346
+ border-radius: calc(var(--radius) - 2px);
347
+ }
348
+ .sh-ui-sidebar__brand-text {
349
+ display: flex;
350
+ flex-direction: column;
351
+ flex: 1 1 0%;
352
+ min-width: 0;
353
+ gap: 0;
354
+ }
355
+ .sh-ui-sidebar__brand-title {
356
+ font-size: var(--text-sm);
357
+ font-weight: var(--weight-medium);
358
+ line-height: 1.25;
359
+ color: var(--sidebar-fg);
360
+ white-space: nowrap;
361
+ overflow: hidden;
362
+ text-overflow: ellipsis;
363
+ }
364
+ .sh-ui-sidebar__brand-subtitle {
365
+ font-size: var(--text-xs);
366
+ line-height: 1.25;
367
+ color: var(--foreground-muted);
368
+ white-space: nowrap;
369
+ overflow: hidden;
370
+ text-overflow: ellipsis;
371
+ }
372
+ .sh-ui-sidebar__brand-action {
373
+ margin-left: auto;
374
+ flex-shrink: 0;
375
+ display: flex;
376
+ align-items: center;
377
+ justify-content: center;
378
+ }
379
+
321
380
  .sh-ui-sidebar__content {
322
381
  display: flex;
323
382
  flex-direction: column;
@@ -83,18 +83,22 @@
83
83
  padding: var(--space-2);
84
84
  }
85
85
 
86
- /* variant: floating */
86
+ /* variant: floating — --sidebar-floating-padding (v0.85.0+) cascaded variable */
87
87
  .sidebar[data-variant="floating"] {
88
88
  border: none;
89
- padding: var(--space-2);
89
+ padding: var(--sidebar-floating-padding, var(--space-2));
90
90
  background: transparent;
91
91
  }
92
92
  .sidebar[data-variant="floating"] .sidebar__inner {
93
93
  border: 1px solid var(--sidebar-border);
94
94
  border-radius: var(--radius);
95
95
  background: var(--sidebar-bg);
96
- height: calc(100svh - 1rem);
97
- top: var(--space-2);
96
+ height: calc(100svh - 2 * var(--sidebar-floating-padding, var(--space-2)));
97
+ top: var(--sidebar-floating-padding, var(--space-2));
98
+ }
99
+ /* DX 2 (v0.85.0+) — floating + icon outer width 자동 보정 */
100
+ .sidebar[data-state="collapsed"][data-collapsible="icon"][data-variant="floating"] {
101
+ width: calc(var(--sidebar-width-icon) + 2 * var(--sidebar-floating-padding, var(--space-2)));
98
102
  }
99
103
 
100
104
  /* variant: inset (메인 영역이 카드 형태) */
@@ -301,6 +305,57 @@
301
305
  background-color: transparent;
302
306
  }
303
307
 
308
+ /* ───────────── Brand (compound primitive, v0.84.0+) ───────────── */
309
+ .sidebar__brand {
310
+ display: flex;
311
+ flex-direction: row;
312
+ align-items: center;
313
+ gap: var(--space-2);
314
+ padding: var(--space-2);
315
+ border-radius: var(--radius);
316
+ min-width: 0;
317
+ }
318
+ .sidebar__brand_icon {
319
+ display: flex;
320
+ align-items: center;
321
+ justify-content: center;
322
+ flex-shrink: 0;
323
+ width: 2rem;
324
+ height: 2rem;
325
+ border-radius: calc(var(--radius) - 2px);
326
+ }
327
+ .sidebar__brand_text {
328
+ display: flex;
329
+ flex-direction: column;
330
+ flex: 1 1 0%;
331
+ min-width: 0;
332
+ gap: 0;
333
+ }
334
+ .sidebar__brand_title {
335
+ font-size: var(--text-sm);
336
+ font-weight: var(--weight-medium);
337
+ line-height: 1.25;
338
+ color: var(--sidebar-fg);
339
+ white-space: nowrap;
340
+ overflow: hidden;
341
+ text-overflow: ellipsis;
342
+ }
343
+ .sidebar__brand_subtitle {
344
+ font-size: var(--text-xs);
345
+ line-height: 1.25;
346
+ color: var(--foreground-muted);
347
+ white-space: nowrap;
348
+ overflow: hidden;
349
+ text-overflow: ellipsis;
350
+ }
351
+ .sidebar__brand_action {
352
+ margin-left: auto;
353
+ flex-shrink: 0;
354
+ display: flex;
355
+ align-items: center;
356
+ justify-content: center;
357
+ }
358
+
304
359
  .sidebar__content {
305
360
  display: flex;
306
361
  flex-direction: column;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$description": "컴포넌트별 토큰 의존성 (var(--*) 추출). build-registry-tokens.mjs 가 자동 생성.",
3
- "$generated": "2026-05-12T23:53:11.079Z",
3
+ "$generated": "2026-05-13T03:22:30.348Z",
4
4
  "components": {
5
5
  "button": {
6
6
  "plain": [
@@ -678,6 +678,7 @@
678
678
  "--duration-fast",
679
679
  "--duration-slow",
680
680
  "--foreground",
681
+ "--foreground-muted",
681
682
  "--opacity-disabled",
682
683
  "--radius",
683
684
  "--shadow-xl",
@@ -688,6 +689,7 @@
688
689
  "--text-lg",
689
690
  "--text-sm",
690
691
  "--text-xs",
692
+ "--weight-medium",
691
693
  "--z-modal",
692
694
  "--z-overlay"
693
695
  ],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh-ui-cli",
3
- "version": "0.83.0",
3
+ "version": "0.85.0",
4
4
  "description": "sh-ui CLI — 프로젝트 스캐폴드(create) + 컴포넌트 추가(add/list/remove) + IDE-내 AI용 MCP 서버",
5
5
  "license": "MIT",
6
6
  "repository": {