sh-ui-cli 0.82.3 → 0.84.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.
- package/data/changelog/versions.json +25 -0
- package/data/registry/react/components/sidebar/index.module.tsx +60 -0
- package/data/registry/react/components/sidebar/index.tailwind.tsx +84 -0
- package/data/registry/react/components/sidebar/index.tsx +86 -0
- package/data/registry/react/components/sidebar/styles.css +81 -0
- package/data/registry/react/components/sidebar/styles.module.css +67 -0
- package/data/registry/react/tokens-used.json +3 -1
- package/package.json +1 -1
|
@@ -2,6 +2,31 @@
|
|
|
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.84.0",
|
|
7
|
+
"date": "2026-05-13",
|
|
8
|
+
"title": "Sidebar — Brand 컴파운드 primitive (브랜드 카드 · 워크스페이스 스위처 · 유저 프로필)",
|
|
9
|
+
"type": "minor",
|
|
10
|
+
"highlights": [
|
|
11
|
+
"**`SidebarBrand` 컴파운드 primitive 신설 — 브랜드 카드 / 워크스페이스 스위처 / 유저 프로필 의 90% 를 커버** — `<SidebarHeader>` / `<SidebarFooter>` 안에서 매번 박던 패턴 (icon + title + subtitle + trailing chevron) 을 단일 합성으로. Card/Dialog 와 동일한 separate-exports 컨벤션.",
|
|
12
|
+
"**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).",
|
|
13
|
+
"**v0.83.0 의 `data-when-collapsed` 컨벤션 내장** — 사용자가 attribute 박지 않아도 collapsible=\"icon\" 모드 전환 시 자동 적응. 인터랙티브 케이스는 `<DropdownMenuTrigger render={<button data-when-collapsed=\"strip-chrome\" />}>` 로 wrap.",
|
|
14
|
+
"**3 변종 모두 지원** — plain `styles.css` / module `styles.module.css` 에 룰 직접, tailwind 변종은 utility 클래스로 동등 스타일. 듀얼카피 (apps/docs) 동기화."
|
|
15
|
+
],
|
|
16
|
+
"url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.84.0"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"version": "0.83.0",
|
|
20
|
+
"date": "2026-05-13",
|
|
21
|
+
"title": "Sidebar — data-when-collapsed 컨벤션 (custom JSX 의 collapsed/icon 자동 적응)",
|
|
22
|
+
"type": "minor",
|
|
23
|
+
"highlights": [
|
|
24
|
+
"**`<SidebarHeader>` / `<SidebarFooter>` 안 사용자 custom JSX 가 `collapsible=\"icon\"` 모드 전환 시 `useSidebar()` 훅 + 조건부 렌더 ceremony 없이 attribute 한 줄로 자동 적응** — 새 export 0개, 발견성 좋은 plain CSS 컨벤션. 값 (공백 구분 다중 토큰, `~=` 매처): `hide` (display:none) · `center` (가운데 정렬 + 좌우 padding 제거) · `strip-chrome` (border / background 투명). 워크스페이스 스위처 · 브랜드 카드 · 유저 프로필 등 admin/dashboard sidebar 의 흔한 패턴에 매번 박던 수동 처리 일소.",
|
|
25
|
+
"예시 — `<button data-when-collapsed=\"center strip-chrome\"> <Avatar /> <div data-when-collapsed=\"hide\">{name}</div> <ChevronsUpDown data-when-collapsed=\"hide\" /></button>` — collapsed 모드에선 Avatar 만 가운데 정렬돼 보이고 카드 chrome 사라짐.",
|
|
26
|
+
"**3 변종 모두 지원** — plain `styles.css` / module `styles.module.css` 에 직접 룰. tailwind 변종은 CSS import 없어 `SidebarProvider` mount 시 1회 head 에 `<style id=\"sh-ui-sidebar-helpers\">` inject (모듈 레벨 dedup). 사용자 코드는 변종 무관 동일 attribute 만 박으면 됨."
|
|
27
|
+
],
|
|
28
|
+
"url": "https://github.com/sanghyeonKim0201/sh-ui/releases/tag/v0.83.0"
|
|
29
|
+
},
|
|
5
30
|
{
|
|
6
31
|
"version": "0.82.3",
|
|
7
32
|
"date": "2026-05-13",
|
|
@@ -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 (
|
|
@@ -10,6 +10,29 @@ const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7;
|
|
|
10
10
|
const SIDEBAR_KEYBOARD_SHORTCUT = "b";
|
|
11
11
|
const MOBILE_BREAKPOINT = 768;
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* v0.83.0+ — `data-when-collapsed` 컨벤션을 위한 CSS 룰 inject.
|
|
15
|
+
*
|
|
16
|
+
* plain/module 변종은 styles.css/.module.css 가 import 되며 자동으로 룰이 깔리는데,
|
|
17
|
+
* tailwind 변종은 별도 CSS import 가 없으므로 SidebarProvider mount 시 한 번만 head
|
|
18
|
+
* 에 <style> 박는다. id="sh-ui-sidebar-helpers" 로 중복 inject 방지.
|
|
19
|
+
*
|
|
20
|
+
* 토큰: hide(display:none) · center(가운데 정렬 + 좌우 padding 0) · strip-chrome
|
|
21
|
+
* (border/bg 투명). `~=` 매처로 공백 구분 다중 토큰 지원.
|
|
22
|
+
*/
|
|
23
|
+
const SIDEBAR_HELPER_STYLE_ID = "sh-ui-sidebar-helpers";
|
|
24
|
+
const SIDEBAR_HELPER_CSS = `[data-state="collapsed"][data-collapsible="icon"] [data-when-collapsed~="hide"]{display:none}[data-state="collapsed"][data-collapsible="icon"] [data-when-collapsed~="center"]{justify-content:center;padding-left:0;padding-right:0}[data-state="collapsed"][data-collapsible="icon"] [data-when-collapsed~="strip-chrome"]{border-color:transparent;background-color:transparent}`;
|
|
25
|
+
function useSidebarHelperStyles() {
|
|
26
|
+
React.useEffect(() => {
|
|
27
|
+
if (typeof document === "undefined") return;
|
|
28
|
+
if (document.getElementById(SIDEBAR_HELPER_STYLE_ID)) return;
|
|
29
|
+
const style = document.createElement("style");
|
|
30
|
+
style.id = SIDEBAR_HELPER_STYLE_ID;
|
|
31
|
+
style.textContent = SIDEBAR_HELPER_CSS;
|
|
32
|
+
document.head.appendChild(style);
|
|
33
|
+
}, []);
|
|
34
|
+
}
|
|
35
|
+
|
|
13
36
|
const FOCUSABLE_SELECTOR =
|
|
14
37
|
'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
15
38
|
|
|
@@ -77,6 +100,7 @@ export interface SidebarProviderProps extends React.HTMLAttributes<HTMLDivElemen
|
|
|
77
100
|
export function SidebarProvider({
|
|
78
101
|
defaultOpen = true, open: openProp, onOpenChange: setOpenProp, className, style, children, embedded, ...props
|
|
79
102
|
}: SidebarProviderProps) {
|
|
103
|
+
useSidebarHelperStyles();
|
|
80
104
|
const isMobile = useIsMobile();
|
|
81
105
|
const [openMobile, setOpenMobile] = React.useState(false);
|
|
82
106
|
const [_open, _setOpen] = React.useState(defaultOpen);
|
|
@@ -328,6 +352,66 @@ export function SidebarFooter({ className, ...props }: React.HTMLAttributes<HTML
|
|
|
328
352
|
return <div className={cn("flex flex-col gap-[var(--space-2)] p-[var(--space-2)] overflow-hidden", className)} {...props} />;
|
|
329
353
|
}
|
|
330
354
|
|
|
355
|
+
/* ───────────── Brand (compound primitive, v0.84.0+) ─────────────
|
|
356
|
+
* data-when-collapsed 컨벤션 내장. plain 변종 주석 참고. */
|
|
357
|
+
|
|
358
|
+
export function SidebarBrand({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
359
|
+
return (
|
|
360
|
+
<div
|
|
361
|
+
data-when-collapsed="center"
|
|
362
|
+
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)}
|
|
363
|
+
{...props}
|
|
364
|
+
/>
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export function SidebarBrandIcon({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
369
|
+
return (
|
|
370
|
+
<div
|
|
371
|
+
className={cn("flex items-center justify-center shrink-0 size-8 rounded-[calc(var(--radius)-2px)]", className)}
|
|
372
|
+
{...props}
|
|
373
|
+
/>
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
export function SidebarBrandText({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
378
|
+
return (
|
|
379
|
+
<div
|
|
380
|
+
data-when-collapsed="hide"
|
|
381
|
+
className={cn("flex flex-col flex-1 min-w-0 gap-0", className)}
|
|
382
|
+
{...props}
|
|
383
|
+
/>
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
export function SidebarBrandTitle({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
388
|
+
return (
|
|
389
|
+
<div
|
|
390
|
+
className={cn("text-[length:var(--text-sm)] font-[var(--weight-medium)] leading-tight truncate text-[color:var(--sidebar-fg)]", className)}
|
|
391
|
+
{...props}
|
|
392
|
+
/>
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export function SidebarBrandSubtitle({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
397
|
+
return (
|
|
398
|
+
<div
|
|
399
|
+
className={cn("text-[length:var(--text-xs)] leading-tight truncate text-[color:var(--foreground-muted)]", className)}
|
|
400
|
+
{...props}
|
|
401
|
+
/>
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export function SidebarBrandAction({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
406
|
+
return (
|
|
407
|
+
<div
|
|
408
|
+
data-when-collapsed="hide"
|
|
409
|
+
className={cn("ml-auto shrink-0 flex items-center justify-center", className)}
|
|
410
|
+
{...props}
|
|
411
|
+
/>
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
|
|
331
415
|
export function SidebarContent({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
|
332
416
|
return <div className={cn("flex flex-col flex-1 min-h-0 overflow-y-auto gap-0", className)} {...props} />;
|
|
333
417
|
}
|
|
@@ -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 (
|
|
@@ -288,6 +288,87 @@
|
|
|
288
288
|
border-bottom: 1px solid var(--sidebar-border);
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
+
/* ───────────── data-when-collapsed 컨벤션 (v0.83.0+) ─────────────
|
|
292
|
+
* SidebarHeader / SidebarFooter 안의 사용자 custom JSX (WorkspaceSwitcher,
|
|
293
|
+
* 브랜드 카드, 유저 프로필 등) 가 collapsible="icon" 모드 전환 시 자동
|
|
294
|
+
* 적응하도록 일반 attribute 컨벤션 제공. 사용자가 useSidebar() 훅 + 조건부
|
|
295
|
+
* 렌더 ceremony 없이 attribute 한 줄로 처리.
|
|
296
|
+
*
|
|
297
|
+
* 사용:
|
|
298
|
+
* <button data-when-collapsed="center strip-chrome">
|
|
299
|
+
* <Avatar />
|
|
300
|
+
* <div data-when-collapsed="hide">{name}</div>
|
|
301
|
+
* <ChevronsUpDown data-when-collapsed="hide" />
|
|
302
|
+
* </button>
|
|
303
|
+
*
|
|
304
|
+
* 값(공백 구분 토큰 — `~=` 매처):
|
|
305
|
+
* hide — collapsed/icon 모드에서 display:none
|
|
306
|
+
* center — 가로 가운데 정렬 + 좌우 padding 제거
|
|
307
|
+
* strip-chrome — border / background 투명화 (카드 chrome 제거) */
|
|
308
|
+
.sh-ui-sidebar[data-state="collapsed"][data-collapsible="icon"] [data-when-collapsed~="hide"] {
|
|
309
|
+
display: none;
|
|
310
|
+
}
|
|
311
|
+
.sh-ui-sidebar[data-state="collapsed"][data-collapsible="icon"] [data-when-collapsed~="center"] {
|
|
312
|
+
justify-content: center;
|
|
313
|
+
padding-left: 0;
|
|
314
|
+
padding-right: 0;
|
|
315
|
+
}
|
|
316
|
+
.sh-ui-sidebar[data-state="collapsed"][data-collapsible="icon"] [data-when-collapsed~="strip-chrome"] {
|
|
317
|
+
border-color: transparent;
|
|
318
|
+
background-color: transparent;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/* ───────────── Brand (compound primitive, v0.84.0+) ───────────── */
|
|
322
|
+
.sh-ui-sidebar__brand {
|
|
323
|
+
display: flex;
|
|
324
|
+
flex-direction: row;
|
|
325
|
+
align-items: center;
|
|
326
|
+
gap: var(--space-2);
|
|
327
|
+
padding: var(--space-2);
|
|
328
|
+
border-radius: var(--radius);
|
|
329
|
+
min-width: 0;
|
|
330
|
+
}
|
|
331
|
+
.sh-ui-sidebar__brand-icon {
|
|
332
|
+
display: flex;
|
|
333
|
+
align-items: center;
|
|
334
|
+
justify-content: center;
|
|
335
|
+
flex-shrink: 0;
|
|
336
|
+
width: 2rem;
|
|
337
|
+
height: 2rem;
|
|
338
|
+
border-radius: calc(var(--radius) - 2px);
|
|
339
|
+
}
|
|
340
|
+
.sh-ui-sidebar__brand-text {
|
|
341
|
+
display: flex;
|
|
342
|
+
flex-direction: column;
|
|
343
|
+
flex: 1 1 0%;
|
|
344
|
+
min-width: 0;
|
|
345
|
+
gap: 0;
|
|
346
|
+
}
|
|
347
|
+
.sh-ui-sidebar__brand-title {
|
|
348
|
+
font-size: var(--text-sm);
|
|
349
|
+
font-weight: var(--weight-medium);
|
|
350
|
+
line-height: 1.25;
|
|
351
|
+
color: var(--sidebar-fg);
|
|
352
|
+
white-space: nowrap;
|
|
353
|
+
overflow: hidden;
|
|
354
|
+
text-overflow: ellipsis;
|
|
355
|
+
}
|
|
356
|
+
.sh-ui-sidebar__brand-subtitle {
|
|
357
|
+
font-size: var(--text-xs);
|
|
358
|
+
line-height: 1.25;
|
|
359
|
+
color: var(--foreground-muted);
|
|
360
|
+
white-space: nowrap;
|
|
361
|
+
overflow: hidden;
|
|
362
|
+
text-overflow: ellipsis;
|
|
363
|
+
}
|
|
364
|
+
.sh-ui-sidebar__brand-action {
|
|
365
|
+
margin-left: auto;
|
|
366
|
+
flex-shrink: 0;
|
|
367
|
+
display: flex;
|
|
368
|
+
align-items: center;
|
|
369
|
+
justify-content: center;
|
|
370
|
+
}
|
|
371
|
+
|
|
291
372
|
.sh-ui-sidebar__content {
|
|
292
373
|
display: flex;
|
|
293
374
|
flex-direction: column;
|
|
@@ -285,6 +285,73 @@
|
|
|
285
285
|
border-bottom: 1px solid var(--sidebar-border);
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
+
/* data-when-collapsed 컨벤션 (v0.83.0+) — 사용자 custom JSX (WorkspaceSwitcher 등) 가
|
|
289
|
+
* collapsible="icon" 모드 전환 시 자동 적응. 값: hide / center / strip-chrome.
|
|
290
|
+
* 자세한 설명은 plain 변종 styles.css 참고. */
|
|
291
|
+
.sidebar[data-state="collapsed"][data-collapsible="icon"] [data-when-collapsed~="hide"] {
|
|
292
|
+
display: none;
|
|
293
|
+
}
|
|
294
|
+
.sidebar[data-state="collapsed"][data-collapsible="icon"] [data-when-collapsed~="center"] {
|
|
295
|
+
justify-content: center;
|
|
296
|
+
padding-left: 0;
|
|
297
|
+
padding-right: 0;
|
|
298
|
+
}
|
|
299
|
+
.sidebar[data-state="collapsed"][data-collapsible="icon"] [data-when-collapsed~="strip-chrome"] {
|
|
300
|
+
border-color: transparent;
|
|
301
|
+
background-color: transparent;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/* ───────────── Brand (compound primitive, v0.84.0+) ───────────── */
|
|
305
|
+
.sidebar__brand {
|
|
306
|
+
display: flex;
|
|
307
|
+
flex-direction: row;
|
|
308
|
+
align-items: center;
|
|
309
|
+
gap: var(--space-2);
|
|
310
|
+
padding: var(--space-2);
|
|
311
|
+
border-radius: var(--radius);
|
|
312
|
+
min-width: 0;
|
|
313
|
+
}
|
|
314
|
+
.sidebar__brand_icon {
|
|
315
|
+
display: flex;
|
|
316
|
+
align-items: center;
|
|
317
|
+
justify-content: center;
|
|
318
|
+
flex-shrink: 0;
|
|
319
|
+
width: 2rem;
|
|
320
|
+
height: 2rem;
|
|
321
|
+
border-radius: calc(var(--radius) - 2px);
|
|
322
|
+
}
|
|
323
|
+
.sidebar__brand_text {
|
|
324
|
+
display: flex;
|
|
325
|
+
flex-direction: column;
|
|
326
|
+
flex: 1 1 0%;
|
|
327
|
+
min-width: 0;
|
|
328
|
+
gap: 0;
|
|
329
|
+
}
|
|
330
|
+
.sidebar__brand_title {
|
|
331
|
+
font-size: var(--text-sm);
|
|
332
|
+
font-weight: var(--weight-medium);
|
|
333
|
+
line-height: 1.25;
|
|
334
|
+
color: var(--sidebar-fg);
|
|
335
|
+
white-space: nowrap;
|
|
336
|
+
overflow: hidden;
|
|
337
|
+
text-overflow: ellipsis;
|
|
338
|
+
}
|
|
339
|
+
.sidebar__brand_subtitle {
|
|
340
|
+
font-size: var(--text-xs);
|
|
341
|
+
line-height: 1.25;
|
|
342
|
+
color: var(--foreground-muted);
|
|
343
|
+
white-space: nowrap;
|
|
344
|
+
overflow: hidden;
|
|
345
|
+
text-overflow: ellipsis;
|
|
346
|
+
}
|
|
347
|
+
.sidebar__brand_action {
|
|
348
|
+
margin-left: auto;
|
|
349
|
+
flex-shrink: 0;
|
|
350
|
+
display: flex;
|
|
351
|
+
align-items: center;
|
|
352
|
+
justify-content: center;
|
|
353
|
+
}
|
|
354
|
+
|
|
288
355
|
.sidebar__content {
|
|
289
356
|
display: flex;
|
|
290
357
|
flex-direction: column;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$description": "컴포넌트별 토큰 의존성 (var(--*) 추출). build-registry-tokens.mjs 가 자동 생성.",
|
|
3
|
-
"$generated": "2026-05-
|
|
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
|
],
|