sh-ui-cli 0.45.2 → 0.46.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.
Files changed (98) hide show
  1. package/data/changelog/versions.json +26 -0
  2. package/data/registry/react/components/accordion/index.tailwind.tsx +5 -7
  3. package/data/registry/react/components/accordion/index.tsx +5 -7
  4. package/data/registry/react/components/avatar/index.tailwind.tsx +4 -6
  5. package/data/registry/react/components/avatar/index.tsx +4 -6
  6. package/data/registry/react/components/badge/index.tailwind.tsx +2 -4
  7. package/data/registry/react/components/badge/index.tsx +2 -4
  8. package/data/registry/react/components/breadcrumb/index.tailwind.tsx +8 -10
  9. package/data/registry/react/components/breadcrumb/index.tsx +8 -10
  10. package/data/registry/react/components/button/index.module.tsx +45 -0
  11. package/data/registry/react/components/button/index.tailwind.tsx +2 -1
  12. package/data/registry/react/components/button/index.tsx +3 -4
  13. package/data/registry/react/components/button/styles.module.css +92 -0
  14. package/data/registry/react/components/calendar/index.tailwind.tsx +10 -12
  15. package/data/registry/react/components/calendar/index.tsx +9 -11
  16. package/data/registry/react/components/card/index.module.tsx +63 -0
  17. package/data/registry/react/components/card/index.tailwind.tsx +8 -10
  18. package/data/registry/react/components/card/index.tsx +8 -10
  19. package/data/registry/react/components/card/styles.module.css +73 -0
  20. package/data/registry/react/components/carousel/index.tailwind.tsx +7 -9
  21. package/data/registry/react/components/carousel/index.tsx +7 -9
  22. package/data/registry/react/components/checkbox/index.tailwind.tsx +3 -5
  23. package/data/registry/react/components/checkbox/index.tsx +3 -5
  24. package/data/registry/react/components/code-editor/index.tailwind.tsx +2 -4
  25. package/data/registry/react/components/code-editor/index.tsx +2 -4
  26. package/data/registry/react/components/code-panel/index.tailwind.tsx +5 -7
  27. package/data/registry/react/components/code-panel/index.tsx +5 -7
  28. package/data/registry/react/components/color-picker/index.tailwind.tsx +7 -6
  29. package/data/registry/react/components/color-picker/index.tsx +7 -6
  30. package/data/registry/react/components/combobox/index.tailwind.tsx +8 -10
  31. package/data/registry/react/components/combobox/index.tsx +8 -10
  32. package/data/registry/react/components/context-menu/index.tailwind.tsx +10 -12
  33. package/data/registry/react/components/context-menu/index.tsx +10 -12
  34. package/data/registry/react/components/date-picker/index.tailwind.tsx +7 -9
  35. package/data/registry/react/components/date-picker/index.tsx +7 -9
  36. package/data/registry/react/components/dialog/index.tailwind.tsx +6 -8
  37. package/data/registry/react/components/dialog/index.tsx +6 -8
  38. package/data/registry/react/components/dropdown-menu/index.tailwind.tsx +10 -12
  39. package/data/registry/react/components/dropdown-menu/index.tsx +10 -12
  40. package/data/registry/react/components/file-upload/index.tailwind.tsx +6 -8
  41. package/data/registry/react/components/file-upload/index.tsx +6 -8
  42. package/data/registry/react/components/form/field.tailwind.tsx +2 -1
  43. package/data/registry/react/components/form/field.tsx +2 -3
  44. package/data/registry/react/components/header/index.tailwind.tsx +17 -19
  45. package/data/registry/react/components/header/index.tsx +17 -19
  46. package/data/registry/react/components/input/index.module.tsx +486 -0
  47. package/data/registry/react/components/input/index.tailwind.tsx +4 -6
  48. package/data/registry/react/components/input/index.tsx +4 -6
  49. package/data/registry/react/components/input/styles.module.css +200 -0
  50. package/data/registry/react/components/label/index.tailwind.tsx +6 -8
  51. package/data/registry/react/components/label/index.tsx +6 -8
  52. package/data/registry/react/components/markdown-editor/index.tailwind.tsx +2 -4
  53. package/data/registry/react/components/markdown-editor/index.tsx +2 -4
  54. package/data/registry/react/components/menubar/index.tailwind.tsx +2 -4
  55. package/data/registry/react/components/menubar/index.tsx +2 -4
  56. package/data/registry/react/components/numeric-input/index.tailwind.tsx +2 -4
  57. package/data/registry/react/components/numeric-input/index.tsx +2 -4
  58. package/data/registry/react/components/page-toc/index.tailwind.tsx +3 -2
  59. package/data/registry/react/components/page-toc/index.tsx +2 -3
  60. package/data/registry/react/components/pagination/index.tailwind.tsx +8 -10
  61. package/data/registry/react/components/pagination/index.tsx +8 -10
  62. package/data/registry/react/components/popover/index.tailwind.tsx +4 -6
  63. package/data/registry/react/components/popover/index.tsx +4 -6
  64. package/data/registry/react/components/progress/index.tailwind.tsx +3 -5
  65. package/data/registry/react/components/progress/index.tsx +2 -4
  66. package/data/registry/react/components/radio/index.tailwind.tsx +3 -5
  67. package/data/registry/react/components/radio/index.tsx +3 -5
  68. package/data/registry/react/components/rich-text-editor/index.tailwind.tsx +3 -5
  69. package/data/registry/react/components/rich-text-editor/index.tsx +3 -5
  70. package/data/registry/react/components/select/index.tailwind.tsx +8 -10
  71. package/data/registry/react/components/select/index.tsx +8 -10
  72. package/data/registry/react/components/separator/index.tailwind.tsx +2 -4
  73. package/data/registry/react/components/separator/index.tsx +2 -4
  74. package/data/registry/react/components/sidebar/index.tailwind.tsx +32 -43
  75. package/data/registry/react/components/sidebar/index.tsx +29 -46
  76. package/data/registry/react/components/skeleton/index.tailwind.tsx +2 -4
  77. package/data/registry/react/components/skeleton/index.tsx +2 -4
  78. package/data/registry/react/components/slider/index.tailwind.tsx +5 -7
  79. package/data/registry/react/components/slider/index.tsx +5 -7
  80. package/data/registry/react/components/spinner/index.tailwind.tsx +3 -5
  81. package/data/registry/react/components/spinner/index.tsx +2 -4
  82. package/data/registry/react/components/switch/index.tailwind.tsx +3 -5
  83. package/data/registry/react/components/switch/index.tsx +2 -4
  84. package/data/registry/react/components/tabs/index.tailwind.tsx +6 -8
  85. package/data/registry/react/components/tabs/index.tsx +6 -8
  86. package/data/registry/react/components/textarea/index.tailwind.tsx +2 -4
  87. package/data/registry/react/components/textarea/index.tsx +2 -4
  88. package/data/registry/react/components/toggle/index.tailwind.tsx +4 -6
  89. package/data/registry/react/components/toggle/index.tsx +4 -6
  90. package/data/registry/react/components/tooltip/index.tailwind.tsx +2 -4
  91. package/data/registry/react/components/tooltip/index.tsx +2 -4
  92. package/data/registry/react/lib/cn.tailwind.ts +17 -0
  93. package/data/registry/react/peer-versions.json +3 -1
  94. package/data/registry/react/registry.json +202 -43
  95. package/data/tokens/build.mjs +4 -0
  96. package/package.json +1 -1
  97. package/src/add.mjs +37 -13
  98. package/templates/ui-app-template/sh-ui.config.json +5 -0
@@ -2,10 +2,8 @@ import * as React from "react";
2
2
  import { Switch as BaseSwitch } from "@base-ui/react/switch";
3
3
  import { cva, type VariantProps } from "class-variance-authority";
4
4
 
5
- function cx(...args: (string | undefined | false)[]) {
6
- return args.filter(Boolean).join(" ");
7
- }
8
5
 
6
+ import { cn } from "@SH_UI_UTILS@";
9
7
  const switchRoot = cva(
10
8
  "inline-flex items-center border-none rounded-full bg-background-muted cursor-pointer shrink-0 p-0.5 transition-colors duration-150 hover:not-data-[disabled]:bg-border-strong focus-visible:outline-[length:var(--border-width-strong)] focus-visible:outline-foreground focus-visible:outline-offset-2 data-[checked]:bg-primary data-[checked]:hover:not-data-[disabled]:bg-primary-hover data-[disabled]:opacity-[var(--opacity-disabled)] data-[disabled]:cursor-not-allowed motion-reduce:transition-none",
11
9
  {
@@ -46,11 +44,11 @@ export const Switch = React.forwardRef<HTMLElement, SwitchProps>(
46
44
  ({ className, size = "md", ...props }, ref) => (
47
45
  <BaseSwitch.Root
48
46
  ref={ref}
49
- className={cx(switchRoot({ size }), className)}
47
+ className={cn(switchRoot({ size }), className)}
50
48
  {...props}
51
49
  >
52
50
  <BaseSwitch.Thumb
53
- className={cx(
51
+ className={cn(
54
52
  switchThumb({ size }),
55
53
  size === "sm" && "data-[checked]:translate-x-3.5",
56
54
  size === "md" && "data-[checked]:translate-x-[1.125rem]",
@@ -2,10 +2,8 @@ import * as React from "react";
2
2
  import { Switch as BaseSwitch } from "@base-ui/react/switch";
3
3
  import "./styles.css";
4
4
 
5
- function cx(...args: (string | undefined | false)[]) {
6
- return args.filter(Boolean).join(" ");
7
- }
8
5
 
6
+ import { cn } from "@SH_UI_UTILS@";
9
7
  /* ───────────── Switch ───────────── */
10
8
 
11
9
  export type SwitchProps = Omit<
@@ -31,7 +29,7 @@ export const Switch = React.forwardRef<HTMLElement, SwitchProps>(
31
29
  ({ className, size = "md", ...props }, ref) => (
32
30
  <BaseSwitch.Root
33
31
  ref={ref}
34
- className={cx("sh-ui-switch", `sh-ui-switch--${size}`, className)}
32
+ className={cn("sh-ui-switch", `sh-ui-switch--${size}`, className)}
35
33
  {...props}
36
34
  >
37
35
  <BaseSwitch.Thumb className="sh-ui-switch__thumb" />
@@ -3,10 +3,8 @@
3
3
  import * as React from "react";
4
4
  import { Tabs as BaseTabs } from "@base-ui/react/tabs";
5
5
 
6
- function cx(...args: (string | undefined | false)[]) {
7
- return args.filter(Boolean).join(" ");
8
- }
9
6
 
7
+ import { cn } from "@SH_UI_UTILS@";
10
8
  type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
11
9
 
12
10
  export type TabsVariant = "underline" | "pill" | "plain";
@@ -26,7 +24,7 @@ export const Tabs = React.forwardRef<HTMLDivElement, TabsProps>(
26
24
  <BaseTabs.Root
27
25
  ref={ref}
28
26
  data-variant={variant}
29
- className={cx("flex flex-col gap-[var(--space-3)] w-full data-[orientation=vertical]:flex-row", className)}
27
+ className={cn("flex flex-col gap-[var(--space-3)] w-full data-[orientation=vertical]:flex-row", className)}
30
28
  {...props}
31
29
  />
32
30
  </TabsContext.Provider>
@@ -46,7 +44,7 @@ export const TabsList = React.forwardRef<
46
44
  <BaseTabs.List
47
45
  ref={ref}
48
46
  data-variant={variant}
49
- className={cx(
47
+ className={cn(
50
48
  "relative inline-flex items-center gap-[var(--space-1)] w-fit",
51
49
  listVariantClasses,
52
50
  className,
@@ -66,7 +64,7 @@ export const TabsTrigger = React.forwardRef<
66
64
  >(({ className, ...props }, ref) => (
67
65
  <BaseTabs.Tab
68
66
  ref={ref}
69
- className={cx(
67
+ className={cn(
70
68
  "relative z-[1] inline-flex items-center justify-center gap-1.5 py-[var(--space-2)] px-[var(--space-3)] bg-transparent text-foreground-muted border-0 text-[length:var(--text-sm)] font-medium leading-none cursor-pointer transition-[color,background-color] duration-[var(--duration-fast)] select-none whitespace-nowrap hover:not-disabled:not-data-[selected]:text-foreground focus-visible:outline-[length:var(--border-width-strong)] focus-visible:outline-foreground focus-visible:outline-offset-2 data-[selected]:text-foreground disabled:opacity-[var(--opacity-disabled)] disabled:cursor-not-allowed",
71
69
  triggerVariantClasses,
72
70
  className,
@@ -85,7 +83,7 @@ export const TabsIndicator = React.forwardRef<
85
83
  >(({ className, ...props }, ref) => (
86
84
  <BaseTabs.Indicator
87
85
  ref={ref}
88
- className={cx(
86
+ className={cn(
89
87
  "absolute z-0 pointer-events-none transition-[top,left,width,height] duration-[180ms] data-[activation-direction=none]:transition-none top-[var(--active-tab-top)] left-[var(--active-tab-left)] w-[var(--active-tab-width)] h-[var(--active-tab-height)]",
90
88
  indicatorVariantClasses,
91
89
  className,
@@ -101,7 +99,7 @@ export const TabsContent = React.forwardRef<
101
99
  >(({ className, ...props }, ref) => (
102
100
  <BaseTabs.Panel
103
101
  ref={ref}
104
- className={cx(
102
+ className={cn(
105
103
  "outline-none focus-visible:outline-[length:var(--border-width-strong)] focus-visible:outline-foreground focus-visible:outline-offset-2 focus-visible:rounded-[var(--radius)]",
106
104
  className,
107
105
  )}
@@ -4,10 +4,8 @@ import * as React from "react";
4
4
  import { Tabs as BaseTabs } from "@base-ui/react/tabs";
5
5
  import "./styles.css";
6
6
 
7
- function cx(...args: (string | undefined | false)[]) {
8
- return args.filter(Boolean).join(" ");
9
- }
10
7
 
8
+ import { cn } from "@SH_UI_UTILS@";
11
9
  type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
12
10
 
13
11
  export type TabsVariant = "underline" | "pill" | "plain";
@@ -41,7 +39,7 @@ export const Tabs = React.forwardRef<HTMLDivElement, TabsProps>(
41
39
  <BaseTabs.Root
42
40
  ref={ref}
43
41
  data-variant={variant}
44
- className={cx("sh-ui-tabs", className)}
42
+ className={cn("sh-ui-tabs", className)}
45
43
  {...props}
46
44
  />
47
45
  </TabsContext.Provider>
@@ -54,7 +52,7 @@ export const TabsList = React.forwardRef<
54
52
  HTMLDivElement,
55
53
  WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseTabs.List>>
56
54
  >(({ className, ...props }, ref) => (
57
- <BaseTabs.List ref={ref} className={cx("sh-ui-tabs__list", className)} {...props} />
55
+ <BaseTabs.List ref={ref} className={cn("sh-ui-tabs__list", className)} {...props} />
58
56
  ));
59
57
  TabsList.displayName = "TabsList";
60
58
 
@@ -63,7 +61,7 @@ export const TabsTrigger = React.forwardRef<
63
61
  HTMLButtonElement,
64
62
  WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseTabs.Tab>>
65
63
  >(({ className, ...props }, ref) => (
66
- <BaseTabs.Tab ref={ref} className={cx("sh-ui-tabs__trigger", className)} {...props} />
64
+ <BaseTabs.Tab ref={ref} className={cn("sh-ui-tabs__trigger", className)} {...props} />
67
65
  ));
68
66
  TabsTrigger.displayName = "TabsTrigger";
69
67
 
@@ -74,7 +72,7 @@ export const TabsIndicator = React.forwardRef<
74
72
  >(({ className, ...props }, ref) => (
75
73
  <BaseTabs.Indicator
76
74
  ref={ref}
77
- className={cx("sh-ui-tabs__indicator", className)}
75
+ className={cn("sh-ui-tabs__indicator", className)}
78
76
  {...props}
79
77
  />
80
78
  ));
@@ -85,7 +83,7 @@ export const TabsContent = React.forwardRef<
85
83
  HTMLDivElement,
86
84
  WithStringClassName<React.ComponentPropsWithoutRef<typeof BaseTabs.Panel>>
87
85
  >(({ className, ...props }, ref) => (
88
- <BaseTabs.Panel ref={ref} className={cx("sh-ui-tabs__content", className)} {...props} />
86
+ <BaseTabs.Panel ref={ref} className={cn("sh-ui-tabs__content", className)} {...props} />
89
87
  ));
90
88
  TabsContent.displayName = "TabsContent";
91
89
 
@@ -1,16 +1,14 @@
1
1
  import * as React from "react";
2
2
 
3
+ import { cn } from "@SH_UI_UTILS@";
3
4
  export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
4
5
 
5
- function cx(...args: (string | undefined | false)[]) {
6
- return args.filter(Boolean).join(" ");
7
- }
8
6
 
9
7
  export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
10
8
  ({ className, ...props }, ref) => (
11
9
  <textarea
12
10
  ref={ref}
13
- className={cx(
11
+ className={cn(
14
12
  "block w-full min-h-20 px-[var(--space-3)] py-[var(--space-2)] bg-background text-foreground border border-border rounded-[var(--radius)] font-[inherit] text-[length:var(--text-sm)] leading-normal resize-y transition-[border-color,box-shadow] duration-[var(--duration-fast)] placeholder:text-foreground-subtle hover:not-disabled:not-focus:border-border-strong focus:outline-none focus:border-foreground focus:shadow-[0_0_0_1px_var(--foreground)] disabled:opacity-[var(--opacity-disabled)] disabled:cursor-not-allowed disabled:bg-background-subtle read-only:bg-background-subtle aria-[invalid=true]:border-danger aria-[invalid=true]:focus:shadow-[0_0_0_1px_var(--danger)] [@media(hover:none)_and_(pointer:coarse)]:text-[length:var(--text-base)]",
15
13
  className,
16
14
  )}
@@ -1,11 +1,9 @@
1
1
  import * as React from "react";
2
2
  import "./styles.css";
3
3
 
4
+ import { cn } from "@SH_UI_UTILS@";
4
5
  export interface TextareaProps extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
5
6
 
6
- function cx(...args: (string | undefined | false)[]) {
7
- return args.filter(Boolean).join(" ");
8
- }
9
7
 
10
8
  /**
11
9
  * 여러 줄 텍스트 입력. 네이티브 `<textarea>` 위에 sh-ui 토큰 스타일만 입혔다.
@@ -16,7 +14,7 @@ export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
16
14
  return (
17
15
  <textarea
18
16
  ref={ref}
19
- className={cx("sh-ui-textarea", className)}
17
+ className={cn("sh-ui-textarea", className)}
20
18
  {...props}
21
19
  />
22
20
  );
@@ -5,10 +5,8 @@ import { Toggle as BaseToggle } from "@base-ui/react/toggle";
5
5
  import { ToggleGroup as BaseToggleGroup } from "@base-ui/react/toggle-group";
6
6
  import { cva, type VariantProps } from "class-variance-authority";
7
7
 
8
- function cx(...args: (string | undefined | false)[]) {
9
- return args.filter(Boolean).join(" ");
10
- }
11
8
 
9
+ import { cn } from "@SH_UI_UTILS@";
12
10
  const toggleVariants = cva(
13
11
  "inline-flex items-center justify-center gap-1.5 border border-transparent rounded-[var(--radius)] font-medium leading-none cursor-pointer text-foreground-muted bg-transparent transition-[background-color,color,border-color] duration-[var(--duration-fast)] select-none focus-visible:outline-[length:var(--border-width-strong)] focus-visible:outline-foreground focus-visible:outline-offset-2 disabled:opacity-[var(--opacity-disabled)] disabled:pointer-events-none data-[pressed]:bg-background-muted data-[pressed]:text-foreground motion-reduce:transition-none",
14
12
  {
@@ -45,7 +43,7 @@ export const Toggle = React.forwardRef<HTMLButtonElement, ToggleProps>(
45
43
  ({ className, variant = "ghost", size = "md", ...props }, ref) => (
46
44
  <BaseToggle
47
45
  ref={ref}
48
- className={cx(toggleVariants({ variant, size }), className)}
46
+ className={cn(toggleVariants({ variant, size }), className)}
49
47
  {...props}
50
48
  />
51
49
  ),
@@ -78,7 +76,7 @@ export const ToggleGroup = React.forwardRef<HTMLDivElement, ToggleGroupProps>(
78
76
  <ToggleGroupContext.Provider value={{ variant, size }}>
79
77
  <BaseToggleGroup
80
78
  ref={ref}
81
- className={cx(
79
+ className={cn(
82
80
  "inline-flex items-center gap-[var(--space-1)] data-[orientation=vertical]:flex-col",
83
81
  className,
84
82
  )}
@@ -102,7 +100,7 @@ export const ToggleGroupItem = React.forwardRef<HTMLButtonElement, ToggleGroupIt
102
100
  return (
103
101
  <BaseToggle
104
102
  ref={ref}
105
- className={cx(toggleVariants({ variant, size }), className)}
103
+ className={cn(toggleVariants({ variant, size }), className)}
106
104
  {...props}
107
105
  />
108
106
  );
@@ -5,10 +5,8 @@ import { Toggle as BaseToggle } from "@base-ui/react/toggle";
5
5
  import { ToggleGroup as BaseToggleGroup } from "@base-ui/react/toggle-group";
6
6
  import "./styles.css";
7
7
 
8
- function cx(...args: (string | undefined | false)[]) {
9
- return args.filter(Boolean).join(" ");
10
- }
11
8
 
9
+ import { cn } from "@SH_UI_UTILS@";
12
10
  /* ───────────── Toggle ───────────── */
13
11
 
14
12
  export type ToggleVariant = "outline" | "ghost";
@@ -43,7 +41,7 @@ export const Toggle = React.forwardRef<HTMLButtonElement, ToggleProps>(
43
41
  ({ className, variant = "ghost", size = "md", ...props }, ref) => (
44
42
  <BaseToggle
45
43
  ref={ref}
46
- className={cx(
44
+ className={cn(
47
45
  "sh-ui-toggle",
48
46
  `sh-ui-toggle--${variant}`,
49
47
  `sh-ui-toggle--${size}`,
@@ -95,7 +93,7 @@ export const ToggleGroup = React.forwardRef<HTMLDivElement, ToggleGroupProps>(
95
93
  <ToggleGroupContext.Provider value={{ variant, size }}>
96
94
  <BaseToggleGroup
97
95
  ref={ref}
98
- className={cx("sh-ui-toggle-group", className)}
96
+ className={cn("sh-ui-toggle-group", className)}
99
97
  {...props}
100
98
  />
101
99
  </ToggleGroupContext.Provider>
@@ -119,7 +117,7 @@ export const ToggleGroupItem = React.forwardRef<HTMLButtonElement, ToggleGroupIt
119
117
  return (
120
118
  <BaseToggle
121
119
  ref={ref}
122
- className={cx(
120
+ className={cn(
123
121
  "sh-ui-toggle",
124
122
  `sh-ui-toggle--${variant}`,
125
123
  `sh-ui-toggle--${size}`,
@@ -1,11 +1,9 @@
1
1
  import * as React from "react";
2
2
  import { Tooltip as BaseTooltip } from "@base-ui/react/tooltip";
3
3
 
4
+ import { cn } from "@SH_UI_UTILS@";
4
5
  type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
5
6
 
6
- function cx(...args: (string | undefined | false | null)[]) {
7
- return args.filter(Boolean).join(" ");
8
- }
9
7
 
10
8
  export const TooltipProvider = BaseTooltip.Provider;
11
9
  export const Tooltip = BaseTooltip.Root;
@@ -39,7 +37,7 @@ export const TooltipContent = React.forwardRef<HTMLDivElement, TooltipContentPro
39
37
  >
40
38
  <BaseTooltip.Popup
41
39
  ref={ref}
42
- className={cx(
40
+ className={cn(
43
41
  "px-2.5 py-1.5 bg-foreground text-background rounded-[calc(var(--radius)-2px)] text-[length:var(--text-xs)] leading-snug max-w-xs shadow-[0_4px_12px_rgba(0,0,0,0.12)] origin-[var(--transform-origin)] outline-none transition-[opacity,transform] duration-[120ms] ease-out motion-reduce:transition-none data-[starting-style]:opacity-0 data-[starting-style]:scale-95 data-[ending-style]:opacity-0 data-[ending-style]:scale-95 motion-reduce:data-[starting-style]:scale-100 motion-reduce:data-[ending-style]:scale-100",
44
42
  className,
45
43
  )}
@@ -2,11 +2,9 @@ import * as React from "react";
2
2
  import { Tooltip as BaseTooltip } from "@base-ui/react/tooltip";
3
3
  import "./styles.css";
4
4
 
5
+ import { cn } from "@SH_UI_UTILS@";
5
6
  type WithStringClassName<T> = Omit<T, "className"> & { className?: string };
6
7
 
7
- function cx(...args: (string | undefined | false | null)[]) {
8
- return args.filter(Boolean).join(" ");
9
- }
10
8
 
11
9
  /** 여러 Tooltip이 공통 delay를 공유하도록 묶는다. 앱 루트에 한 번 두는 것을 권장. */
12
10
  export const TooltipProvider = BaseTooltip.Provider;
@@ -71,7 +69,7 @@ export const TooltipContent = React.forwardRef<
71
69
  >
72
70
  <BaseTooltip.Popup
73
71
  ref={ref}
74
- className={cx("sh-ui-tooltip__content", className)}
72
+ className={cn("sh-ui-tooltip__content", className)}
75
73
  {...props}
76
74
  >
77
75
  {showArrow && (
@@ -0,0 +1,17 @@
1
+ /**
2
+ * className 합성 헬퍼 (Tailwind 변종) — clsx + tailwind-merge.
3
+ * - falsy 값 자동 제외
4
+ * - 객체 형태 `{ "is-active": true }` 지원
5
+ * - **Tailwind utility 충돌 머지** — `cn("bg-red-500", "bg-blue-500")` → `"bg-blue-500"`
6
+ * 사용자가 `<Button className="bg-red-500" />` 로 default 를 깔끔히 override 가능.
7
+ *
8
+ * cn("base", isActive && "active", { "with-icon": hasIcon }, className)
9
+ */
10
+ import { clsx, type ClassValue } from "clsx";
11
+ import { twMerge } from "tailwind-merge";
12
+
13
+ export type { ClassValue };
14
+
15
+ export function cn(...inputs: ClassValue[]): string {
16
+ return twMerge(clsx(inputs));
17
+ }
@@ -4,8 +4,10 @@
4
4
  "@base-ui/react": "^1.4.1",
5
5
  "@tanstack/react-form": "^1.29.1",
6
6
  "class-variance-authority": "^0.7.1",
7
+ "clsx": "^2.1.1",
7
8
  "lucide-react": "^1.11.0",
8
9
  "react-hook-form": "^7.74.0",
9
- "shiki": "^4.0.2"
10
+ "shiki": "^4.0.2",
11
+ "tailwind-merge": "^3.5.0"
10
12
  }
11
13
  }