@olundot/ui 0.18.0 → 0.19.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/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # @olundot/ui
2
2
 
3
+ ## 0.19.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 42d2650: `Button`·`IconButton`에 새 축 `appearance`(`filled`·`outline`·`ghost`) × `tone`(`primary`·`neutral`·`danger`)을 추가했습니다. 옛 `variant`는 0.18과 똑같이 그려지고, 사용 중단 예정으로 표시됩니다. 0.20에서 삭제합니다.
8
+
9
+ - **쓸 수 있는 조합 다섯 개** — `filled` × `primary`·`neutral`·`danger`, `outline` × `neutral`, `ghost` × `neutral`. 나머지 네 조합은 타입 오류입니다. 새 축과 `variant`를 함께 줘도 타입 오류입니다.
10
+ - **두 컴포넌트가 같은 모양** — 같은 조합이면 `Button`과 `IconButton`의 색·테두리·hover가 같습니다. 크기는 아래 "크기 단계 하나"를 따릅니다.
11
+ - **기본값** — 아무것도 주지 않으면 지금과 같습니다(`Button` = `filled` + `primary`, `IconButton` = `ghost` + `neutral`). 한쪽만 주면 나머지를 채웁니다. `tone`이 없으면 `filled`는 `primary`, `outline`·`ghost`는 `neutral`입니다.
12
+ - **`IconButton` 글자** — `label="추가"` 또는 children으로 글자를 넣습니다. 둘을 함께 주면 타입 오류입니다. 글자가 있으면 `aria-label`은 필수가 아니고, 폭은 글자에 맞춥니다. 아이콘 위치는 `iconPosition="start" | "end"`(기본 `start`)입니다. 아이콘만일 때는 지금처럼 정사각형이고 누르는 영역은 44px입니다.
13
+ - **크기 단계 하나** — `appearance`나 `tone`을 준 `IconButton`은 `size`를 `Button`과 같은 5단계로 읽습니다: `xs` 28 · `sm` 30 · `md` 32 · `lg` 36 · `xl` 40(기본 `md`). 같은 `size`면 `Button`·글자 포함 `IconButton`·아이콘만 `IconButton`의 높이와 아이콘 크기·선 굵기가 같습니다(`xs` 14/1.75, `sm`·`md` 16/2, `lg`·`xl` 18/2). 아이콘만은 정사각형이고 누르는 영역은 사방 44px입니다.
14
+ - **옛 크기** — 축을 주지 않은 `IconButton`은 지금처럼 3단계 `sm` 28 · `md` 32 · `lg` 40으로 읽고 출력도 0.18과 같습니다. 새 축으로 옮길 때 `sm` → `xs`, `lg` → `xl`로 바꿉니다(`md`는 그대로). 축 없이 `xs`·`xl`을 주면 타입 오류입니다. 축 없이 글자를 넣으면 높이는 같은 높이의 `Button`(`xs`·`md`·`xl`)을 따릅니다.
15
+ - **옛 값 → 새 값**
16
+ - `Button` `variant`: `primary`·`default` → `filled` + `primary` / `secondary`·`outline`·`subtle` → `outline` + `neutral` / `ghost` → `ghost` + `neutral` / `danger`·`destructive`·`emergency` → `filled` + `danger` / `link` → 대체 없음(링크 컴포넌트 예정)
17
+ - `Button` `size="icon"`·`"icon-xs"` → 새 축 `IconButton` `lg`·`md`(36·32, 높이 그대로), `leadingIcon`·`trailingIcon` → 글자 포함 `IconButton`
18
+ - `IconButton` `variant`: `ghost` → `ghost` + `neutral` / `outline` → `outline` + `neutral` / `subtle` → `filled` + `neutral` / `danger` → `filled` + `danger`. `size`는 `sm` → `xs`, `lg` → `xl`
19
+ - **옮길 때 모양이 바뀌는 곳** — `IconButton` `variant="danger"`(연한 빨강)는 `filled` + `danger`(진한 빨강 채움)로 바뀝니다. `IconButton` `variant="outline"`은 `outline` + `neutral`로 옮기면 아이콘 색이 `text-secondary`에서 `foreground`로, hover 테두리가 `--border-strong`에서 `--border`로 바뀝니다(`Button` `secondary`와 같은 모양). 옛 값을 그대로 쓰는 동안에는 바뀌지 않습니다.
20
+ - `ButtonProps`는 interface에서 type(union)으로 바뀌었습니다. `interface X extends ButtonProps`로 확장하던 곳은 `type X = ButtonProps & { … }`로 바꿔야 합니다.
21
+
22
+ ### Patch Changes
23
+
24
+ - 2e965c7: OlunSymbol now has two optical sizes. Above 32px it follows the brand concept sheet (thicker ring, smaller dot clearly apart from the ring); at 32px and below it keeps the current icon geometry.
25
+
26
+ OlunLogo drops the wordmark period when the symbol is shown, so the lime orbit dot is not repeated. The wordmark alone still reads `OLUN.`.
27
+
28
+ - 11d8450: Tabs: 세로 탭 활성 표시가 구분선 위에 겹치도록 수정하고, `sm` 미만에서는 가로 탭 규칙으로 표시합니다. line 탭의 focus-visible ring이 목록 overflow에 잘리지 않도록 ring을 안쪽으로 그립니다.
29
+ - Updated dependencies [89a385a]
30
+ - Updated dependencies [2fa68e9]
31
+ - @olundot/tokens@0.17.0
32
+
3
33
  ## 0.18.0
4
34
 
5
35
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -31,26 +31,60 @@ declare function useOlunTheme(): UseOlunThemeValue;
31
31
 
32
32
  type SurfaceLevel = "surface" | "canvas" | "raised";
33
33
 
34
- declare const buttonVariants: (props?: ({
35
- variant?: "default" | "primary" | "outline" | "ghost" | "danger" | "emergency" | "subtle" | "destructive" | "secondary" | "link" | null | undefined;
36
- size?: "default" | "xs" | "sm" | "md" | "lg" | "xl" | "icon" | "icon-xs" | null | undefined;
34
+ type ActionAppearance = 'filled' | 'outline' | 'ghost';
35
+ type ActionTone = 'primary' | 'neutral' | 'danger';
36
+ type FilledDefaultActionStyle = {
37
+ appearance?: 'filled';
38
+ tone?: ActionTone;
39
+ } | {
40
+ appearance: 'outline' | 'ghost';
41
+ tone?: 'neutral';
42
+ };
43
+ type GhostDefaultActionStyle = {
44
+ appearance: 'filled';
45
+ tone?: ActionTone;
46
+ } | {
47
+ appearance: 'outline' | 'ghost';
48
+ tone?: 'neutral';
49
+ } | {
50
+ appearance?: never;
51
+ tone: 'neutral';
52
+ };
53
+
54
+ declare const buttonCva: (props?: ({
55
+ size?: "default" | "md" | "xs" | "sm" | "lg" | "xl" | "icon" | "icon-xs" | null | undefined;
56
+ appearance?: "filled" | "outline" | "ghost" | null | undefined;
57
+ tone?: "primary" | "neutral" | "danger" | null | undefined;
58
+ variant?: "default" | "outline" | "ghost" | "primary" | "danger" | "emergency" | "subtle" | "destructive" | "secondary" | "link" | null | undefined;
37
59
  } & class_variance_authority_types.ClassProp) | undefined) => string;
60
+ type ButtonVariantsOptions = Parameters<typeof buttonCva>[0];
61
+ declare function buttonVariants(options?: ButtonVariantsOptions): string;
38
62
  type ButtonVariant = NonNullable<VariantProps<typeof buttonVariants>['variant']>;
39
63
  type ButtonSize = NonNullable<VariantProps<typeof buttonVariants>['size']>;
40
- interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement>, VariantProps<typeof buttonVariants> {
64
+ type ButtonStyleProps = (FilledDefaultActionStyle & {
41
65
  /**
42
- * Action hierarchy: `primary` (the one main decision) · `secondary` (every supporting
43
- * action) · `ghost` (icon-only chrome — prefer IconButton) · `danger` (destructive).
44
- *
45
- * `outline` and `subtle` are deprecated — use `secondary`. They will be removed in the
46
- * next breaking release.
66
+ * @deprecated Use `appearance` × `tone` instead. Removed in 0.20.
67
+ */
68
+ variant?: never;
69
+ }) | {
70
+ /**
71
+ * @deprecated Use `appearance` × `tone`: `primary`/`default` → filled + primary ·
72
+ * `secondary`/`outline`/`subtle` → outline + neutral · `ghost` → ghost + neutral ·
73
+ * `danger`/`destructive`/`emergency` → filled + danger · `link` → no Button replacement
74
+ * (a link component is planned). Removed in 0.20.
47
75
  */
48
- variant?: VariantProps<typeof buttonVariants>['variant'];
76
+ variant?: ButtonVariant | null;
77
+ };
78
+ type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement> & ButtonStyleProps & {
79
+ /** `icon`·`icon-xs` are deprecated — use IconButton (`lg`·`md`). */
80
+ size?: ButtonSize | null;
49
81
  asChild?: boolean;
82
+ /** @deprecated Use IconButton with `label` (or children). Removed in 0.20. */
50
83
  leadingIcon?: LucideIcon;
84
+ /** @deprecated Use IconButton with `label` and `iconPosition="end"`. Removed in 0.20. */
51
85
  trailingIcon?: LucideIcon;
52
86
  loading?: boolean;
53
- }
87
+ };
54
88
  declare const Button: React.ForwardRefExoticComponent<ButtonProps & React.RefAttributes<HTMLButtonElement>>;
55
89
 
56
90
  type CopyButtonProps = Omit<ButtonProps, 'children' | 'leadingIcon' | 'loading' | 'size' | 'trailingIcon' | 'variant'> & {
@@ -62,7 +96,7 @@ type CopyButtonProps = Omit<ButtonProps, 'children' | 'leadingIcon' | 'loading'
62
96
  variant?: ButtonVariant;
63
97
  duration?: number;
64
98
  };
65
- declare const CopyButton: React.ForwardRefExoticComponent<Omit<ButtonProps, "size" | "variant" | "leadingIcon" | "trailingIcon" | "loading" | "children"> & {
99
+ declare const CopyButton: React.ForwardRefExoticComponent<Omit<ButtonProps, "size" | "variant" | "children" | "leadingIcon" | "trailingIcon" | "loading"> & {
66
100
  value: string;
67
101
  label?: React.ReactNode;
68
102
  copiedLabel?: React.ReactNode;
@@ -73,13 +107,13 @@ declare const CopyButton: React.ForwardRefExoticComponent<Omit<ButtonProps, "siz
73
107
  } & React.RefAttributes<HTMLButtonElement>>;
74
108
 
75
109
  declare const inputVariants: (props?: ({
76
- size?: "sm" | "md" | "lg" | null | undefined;
110
+ size?: "md" | "sm" | "lg" | null | undefined;
77
111
  fullWidth?: boolean | null | undefined;
78
112
  surface?: SurfaceLevel | null | undefined;
79
113
  } & class_variance_authority_types.ClassProp) | undefined) => string;
80
114
  declare const inputMessageVariants: (props?: ({
81
115
  state?: "default" | "error" | "success" | null | undefined;
82
- size?: "sm" | "md" | "lg" | null | undefined;
116
+ size?: "md" | "sm" | "lg" | null | undefined;
83
117
  } & class_variance_authority_types.ClassProp) | undefined) => string;
84
118
  type InputState = NonNullable<VariantProps<typeof inputMessageVariants>["state"]>;
85
119
  /** Canonical size type (0.2.0+). Use `size` prop with these values. */
@@ -169,7 +203,7 @@ interface InlineAlertProps extends Omit<HTMLAttributes<HTMLDivElement>, 'childre
169
203
  declare const InlineAlert: React.ForwardRefExoticComponent<InlineAlertProps & React.RefAttributes<HTMLDivElement>>;
170
204
 
171
205
  declare const textareaVariants: (props?: ({
172
- size?: "sm" | "md" | "lg" | null | undefined;
206
+ size?: "md" | "sm" | "lg" | null | undefined;
173
207
  fullWidth?: boolean | null | undefined;
174
208
  surface?: SurfaceLevel | null | undefined;
175
209
  } & class_variance_authority_types.ClassProp) | undefined) => string;
@@ -223,14 +257,14 @@ interface TextareaProps extends Omit<React.TextareaHTMLAttributes<HTMLTextAreaEl
223
257
  declare const Textarea: React.ForwardRefExoticComponent<TextareaProps & React.RefAttributes<HTMLTextAreaElement>>;
224
258
 
225
259
  declare const selectVariants: (props?: ({
226
- size?: "sm" | "md" | "lg" | null | undefined;
260
+ size?: "md" | "sm" | "lg" | null | undefined;
227
261
  fullWidth?: boolean | null | undefined;
228
262
  appearance?: "inline" | "field" | null | undefined;
229
263
  surface?: SurfaceLevel | null | undefined;
230
264
  } & class_variance_authority_types.ClassProp) | undefined) => string;
231
265
  declare const selectMessageVariants: (props?: ({
232
266
  state?: "default" | "error" | "success" | null | undefined;
233
- size?: "sm" | "md" | "lg" | null | undefined;
267
+ size?: "md" | "sm" | "lg" | null | undefined;
234
268
  } & class_variance_authority_types.ClassProp) | undefined) => string;
235
269
  type SelectState = NonNullable<VariantProps<typeof selectMessageVariants>["state"]>;
236
270
  /** Canonical size type (0.2.0+). Use `size` prop with these values. */
@@ -1168,7 +1202,7 @@ interface StatusBadgeProps extends React.HTMLAttributes<HTMLSpanElement> {
1168
1202
  declare const StatusBadge: React.ForwardRefExoticComponent<StatusBadgeProps & React.RefAttributes<HTMLSpanElement>>;
1169
1203
 
1170
1204
  declare const notificationBadgeVariants: (props?: ({
1171
- size?: "sm" | "md" | null | undefined;
1205
+ size?: "md" | "sm" | null | undefined;
1172
1206
  placement?: "none" | "inline" | "corner" | null | undefined;
1173
1207
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1174
1208
  type NotificationBadgeSize = NonNullable<VariantProps<typeof notificationBadgeVariants>['size']>;
@@ -1181,13 +1215,13 @@ declare const NotificationBadge: React.ForwardRefExoticComponent<NotificationBad
1181
1215
 
1182
1216
  declare const chipVariants: (props?: ({
1183
1217
  variant?: "filter" | "choice" | "removable" | "status-filter" | null | undefined;
1184
- tone?: "accent" | "danger" | "success" | "warning" | "neutral" | "muted" | null | undefined;
1185
- size?: "sm" | "md" | "lg" | null | undefined;
1218
+ tone?: "accent" | "neutral" | "danger" | "success" | "warning" | "muted" | null | undefined;
1219
+ size?: "md" | "sm" | "lg" | null | undefined;
1186
1220
  pressed?: boolean | null | undefined;
1187
1221
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1188
1222
  declare const chipCountVariants: (props?: ({
1189
- tone?: "accent" | "danger" | "success" | "warning" | "neutral" | "muted" | null | undefined;
1190
- size?: "sm" | "md" | "lg" | null | undefined;
1223
+ tone?: "accent" | "neutral" | "danger" | "success" | "warning" | "muted" | null | undefined;
1224
+ size?: "md" | "sm" | "lg" | null | undefined;
1191
1225
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1192
1226
  type ChipVariant = NonNullable<VariantProps<typeof chipVariants>["variant"]>;
1193
1227
  type ChipTone = NonNullable<VariantProps<typeof chipVariants>["tone"]>;
@@ -1201,7 +1235,7 @@ interface ChipProps extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>,
1201
1235
  declare const Chip: React.ForwardRefExoticComponent<ChipProps & React.RefAttributes<HTMLButtonElement>>;
1202
1236
 
1203
1237
  declare const emptyStateBaseVariants: (props?: ({
1204
- size?: "sm" | "md" | "lg" | null | undefined;
1238
+ size?: "md" | "sm" | "lg" | null | undefined;
1205
1239
  bordered?: boolean | null | undefined;
1206
1240
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1207
1241
  type EmptyStateTone = "neutral" | "muted" | "raised";
@@ -1212,7 +1246,7 @@ interface EmptyStateVariantOptions extends VariantProps<typeof emptyStateBaseVar
1212
1246
  }
1213
1247
  declare function emptyStateVariants({ size, tone, surface, bordered, className, }?: EmptyStateVariantOptions): string;
1214
1248
  declare const emptyStateIconVariants: (props?: ({
1215
- size?: "sm" | "md" | "lg" | null | undefined;
1249
+ size?: "md" | "sm" | "lg" | null | undefined;
1216
1250
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1217
1251
  type EmptyStateSize = NonNullable<VariantProps<typeof emptyStateBaseVariants>["size"]>;
1218
1252
  interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">, VariantProps<typeof emptyStateBaseVariants> {
@@ -1231,7 +1265,7 @@ interface EmptyStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "ti
1231
1265
  declare const EmptyState: React.ForwardRefExoticComponent<EmptyStateProps & React.RefAttributes<HTMLDivElement>>;
1232
1266
 
1233
1267
  declare const errorStateVariants: (props?: ({
1234
- size?: "sm" | "md" | "lg" | null | undefined;
1268
+ size?: "md" | "sm" | "lg" | null | undefined;
1235
1269
  surface?: SurfaceLevel | null | undefined;
1236
1270
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1237
1271
  interface ErrorStateProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "title">, VariantProps<typeof errorStateVariants> {
@@ -1251,7 +1285,7 @@ declare const skeletonVariants: (props?: ({
1251
1285
  shape?: "text" | "block" | "circle" | null | undefined;
1252
1286
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1253
1287
  declare const spinnerVariants: (props?: ({
1254
- size?: "sm" | "md" | "lg" | 16 | 24 | 32 | null | undefined;
1288
+ size?: "md" | "sm" | "lg" | 16 | 24 | 32 | null | undefined;
1255
1289
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1256
1290
  interface SkeletonProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof skeletonVariants> {
1257
1291
  }
@@ -1283,16 +1317,29 @@ interface SpinnerProps extends React.HTMLAttributes<HTMLSpanElement> {
1283
1317
  }
1284
1318
  declare const Spinner: React.ForwardRefExoticComponent<SpinnerProps & React.RefAttributes<HTMLSpanElement>>;
1285
1319
 
1286
- declare const iconButtonVariants: (props?: ({
1287
- variant?: "outline" | "ghost" | "danger" | "subtle" | null | undefined;
1288
- size?: "sm" | "md" | "lg" | null | undefined;
1320
+ declare const iconButtonCva: (props?: ({
1321
+ size?: "md" | "sm" | "lg" | null | undefined;
1289
1322
  pressed?: boolean | null | undefined;
1323
+ appearance?: "filled" | "outline" | "ghost" | null | undefined;
1324
+ tone?: "primary" | "neutral" | "danger" | null | undefined;
1325
+ variant?: "outline" | "ghost" | "danger" | "subtle" | null | undefined;
1290
1326
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1327
+ type IconButtonAxisSize = "xs" | "sm" | "md" | "lg" | "xl";
1328
+ type IconButtonCvaOptions = NonNullable<Parameters<typeof iconButtonCva>[0]>;
1329
+ type IconButtonVariantsOptions = (IconButtonCvaOptions & {
1330
+ appearance?: null;
1331
+ tone?: null;
1332
+ }) | (Omit<IconButtonCvaOptions, "size"> & {
1333
+ appearance?: ActionAppearance | null;
1334
+ tone?: ActionTone | null;
1335
+ size?: IconButtonAxisSize | null;
1336
+ });
1337
+ declare function iconButtonVariants(options?: IconButtonVariantsOptions): string;
1291
1338
  declare const iconButtonBadgeVariants: (props?: ({
1292
1339
  tone?: "error" | "info" | null | undefined;
1293
1340
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1294
- type IconButtonVariant = NonNullable<VariantProps<typeof iconButtonVariants>["variant"]>;
1295
- type IconButtonSize = NonNullable<VariantProps<typeof iconButtonVariants>["size"]>;
1341
+ type IconButtonVariant = NonNullable<VariantProps<typeof iconButtonCva>["variant"]>;
1342
+ type IconButtonSize = NonNullable<VariantProps<typeof iconButtonCva>["size"]>;
1296
1343
  type IconButtonBadgeTone = NonNullable<VariantProps<typeof iconButtonBadgeVariants>["tone"]>;
1297
1344
  type IconButtonIcon = React.ComponentType<React.SVGProps<SVGSVGElement> & {
1298
1345
  size?: number | string;
@@ -1305,11 +1352,47 @@ type IconButtonAccessibleName = {
1305
1352
  "aria-label"?: string;
1306
1353
  "aria-labelledby": string;
1307
1354
  };
1308
- type IconButtonProps = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "aria-label" | "aria-labelledby" | "children" | "color"> & Omit<VariantProps<typeof iconButtonVariants>, "pressed"> & IconButtonAccessibleName & {
1355
+ type IconButtonOptionalName = {
1356
+ "aria-label"?: string;
1357
+ "aria-labelledby"?: string;
1358
+ };
1359
+ type IconButtonContent = (IconButtonAccessibleName & {
1360
+ label?: never;
1361
+ children?: never;
1362
+ }) | (IconButtonOptionalName & {
1363
+ label: React.ReactNode;
1364
+ children?: never;
1365
+ }) | (IconButtonOptionalName & {
1366
+ children: React.ReactNode;
1367
+ label?: never;
1368
+ });
1369
+ type IconButtonStyleProps = (GhostDefaultActionStyle & {
1370
+ /**
1371
+ * @deprecated Use `appearance` × `tone` instead. Removed in 0.20.
1372
+ */
1373
+ variant?: never;
1374
+ /** Button's scale: `xs` 28 · `sm` 30 · `md` 32 · `lg` 36 · `xl` 40. */
1375
+ size?: IconButtonAxisSize | null;
1376
+ }) | {
1377
+ /**
1378
+ * @deprecated Use `appearance` × `tone`: `ghost` → ghost + neutral · `outline` →
1379
+ * outline + neutral · `subtle` → filled + neutral · `danger` → filled + danger
1380
+ * (solid red; `danger` keeps its light red until removal). When moving to the new axes,
1381
+ * `size` follows Button's scale: `sm` → `xs`, `lg` → `xl` (`md` stays). Removed in 0.20.
1382
+ */
1383
+ variant?: IconButtonVariant | null;
1384
+ appearance?: never;
1385
+ tone?: never;
1386
+ /** Legacy scale without `appearance`/`tone`: `sm` 28 · `md` 32 · `lg` 40. */
1387
+ size?: IconButtonSize | null;
1388
+ };
1389
+ type IconButtonProps = Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "aria-label" | "aria-labelledby" | "children" | "color"> & IconButtonStyleProps & IconButtonContent & {
1309
1390
  icon: IconButtonIcon;
1310
1391
  pressed?: boolean;
1311
1392
  badge?: React.ReactNode;
1312
1393
  badgeTone?: IconButtonBadgeTone;
1394
+ /** Icon side when the button has text. */
1395
+ iconPosition?: "start" | "end";
1313
1396
  };
1314
1397
  declare const IconButton: React.ForwardRefExoticComponent<IconButtonProps & React.RefAttributes<HTMLButtonElement>>;
1315
1398
 
@@ -1518,10 +1601,10 @@ interface OTPFieldProps extends Omit<React.FieldsetHTMLAttributes<HTMLFieldSetEl
1518
1601
  declare const OTPField: React.ForwardRefExoticComponent<OTPFieldProps & React.RefAttributes<HTMLFieldSetElement>>;
1519
1602
 
1520
1603
  declare const searchFieldVariants: (props?: ({
1521
- size?: "sm" | "md" | "lg" | null | undefined;
1604
+ size?: "md" | "sm" | "lg" | null | undefined;
1522
1605
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1523
1606
  declare const searchFieldInputVariants: (props?: ({
1524
- size?: "sm" | "md" | "lg" | null | undefined;
1607
+ size?: "md" | "sm" | "lg" | null | undefined;
1525
1608
  hasClear?: boolean | null | undefined;
1526
1609
  surface?: SurfaceLevel | null | undefined;
1527
1610
  } & class_variance_authority_types.ClassProp) | undefined) => string;
@@ -1545,7 +1628,7 @@ declare const checkboxVariants: (props?: ({
1545
1628
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1546
1629
  declare const checkboxRootVariants: (props?: class_variance_authority_types.ClassProp | undefined) => string;
1547
1630
  declare const checkboxBoxVariants: (props?: ({
1548
- size?: "sm" | "md" | null | undefined;
1631
+ size?: "md" | "sm" | null | undefined;
1549
1632
  } & class_variance_authority_types.ClassProp) | undefined) => string;
1550
1633
  declare const checkboxGroupItemVariants: (props?: ({
1551
1634
  appearance?: "plain" | "card" | null | undefined;
@@ -1699,8 +1782,9 @@ declare function TreeView<T extends TreeNode>({ nodes, selectedId, selectionAria
1699
1782
  /**
1700
1783
  * OLUN 브랜드 로고.
1701
1784
  *
1702
- * - symbol: O 원형 테두리 + 우하단 accent-pop 작은 circle
1785
+ * - symbol: O 원형 테두리 + 우하단 accent-pop 작은 circle (32px 초과/이하 두 비율)
1703
1786
  * - wordmark: "OLUN" + 마지막 "." 은 accent 색상
1787
+ * - 조합: symbol과 함께 쓰면 wordmark의 "."를 뺀다. 궤도 점은 symbol이 맡는다(점 두 개 경쟁 방지).
1704
1788
  *
1705
1789
  * 레퍼런스: apps/web/docs/wireframes/ref/shell-wireframe.html Topbar 브랜드 영역.
1706
1790
  */
package/dist/index.js CHANGED
@@ -186,8 +186,61 @@ import * as React from "react";
186
186
  import { cva } from "class-variance-authority";
187
187
  import { Loader2 } from "lucide-react";
188
188
  import { Slot } from "radix-ui";
189
+
190
+ // src/components/ui/action-variants.ts
191
+ var actionPaint = {
192
+ // = Button `primary`
193
+ filledPrimary: "border-[var(--primary)] bg-[var(--primary)] text-[var(--primary-fg)] hover:bg-[var(--primary-hover)] hover:border-[var(--primary-hover)]",
194
+ // = IconButton `subtle`; the transparent 1px border keeps Button's box model
195
+ filledNeutral: "border-transparent bg-[var(--bg-inset)] text-text-secondary hover:bg-[var(--action-hover)] hover:text-foreground",
196
+ // = Button `danger`
197
+ filledDanger: "border-[var(--error)] bg-[var(--error)] text-[var(--error-contrast)] hover:bg-[var(--error-hover)] hover:border-[var(--error-hover)]",
198
+ // = Button `secondary`
199
+ outlineNeutral: "border-[var(--border-subtle)] bg-[var(--bg-surface)] text-foreground hover:border-[var(--border)] hover:bg-[var(--action-hover)]",
200
+ // = Button and IconButton `ghost`
201
+ ghostNeutral: "border-transparent bg-transparent text-text-secondary hover:bg-[var(--action-hover)] hover:text-foreground"
202
+ };
203
+ var actionAxisVariants = {
204
+ appearance: { filled: "", outline: "", ghost: "" },
205
+ tone: { primary: "", neutral: "", danger: "" }
206
+ };
207
+ var actionCompoundVariants = [
208
+ { appearance: "filled", tone: "primary", className: actionPaint.filledPrimary },
209
+ { appearance: "filled", tone: "neutral", className: actionPaint.filledNeutral },
210
+ { appearance: "filled", tone: "danger", className: actionPaint.filledDanger },
211
+ { appearance: "outline", tone: "neutral", className: actionPaint.outlineNeutral },
212
+ { appearance: "ghost", tone: "neutral", className: actionPaint.ghostNeutral }
213
+ ];
214
+ function resolveActionStyle(appearance, tone, fallback) {
215
+ const resolvedAppearance = appearance ?? fallback;
216
+ return {
217
+ appearance: resolvedAppearance,
218
+ tone: tone ?? (resolvedAppearance === "filled" ? "primary" : "neutral")
219
+ };
220
+ }
221
+
222
+ // src/components/ui/button.tsx
189
223
  import { jsx, jsxs } from "react/jsx-runtime";
190
- var buttonVariants = cva(
224
+ var buttonSizeMd = "h-[32px] gap-[6px] px-pad-default-x text-body-sm [&_svg]:size-[16px]";
225
+ var buttonSizes = {
226
+ // Cross-control height map: Button sm/md/lg match field controls sm/md/lg (30/32/36px).
227
+ // Height comes from h-* alone — no vertical padding (py-12 inside h-32 left a 6px content box).
228
+ // xs/sm: invisible ::before stretches the hit area to 44px tall (#69), vertically only so
229
+ // side-by-side buttons never overlap. Its inset is measured from the padding box, inside
230
+ // the 1px border every variant carries: inset = (44 - (height - 2 × 1px)) / 2.
231
+ // IconButton with text reuses these through `buttonSizeClassName`.
232
+ xs: "relative h-[28px] gap-[6px] px-pad-compact-x text-[12px] before:absolute before:inset-x-0 before:-inset-y-[9px] before:content-[''] [&_svg]:size-[14px] [&_svg]:stroke-[1.75]",
233
+ sm: "relative h-[30px] gap-[6px] px-pad-compact-x text-body-sm before:absolute before:inset-x-0 before:-inset-y-[8px] before:content-[''] [&_svg]:size-[16px]",
234
+ md: buttonSizeMd,
235
+ lg: "h-[36px] gap-[var(--space-2)] px-pad-default-x text-[15px] [&_svg]:size-[18px]",
236
+ xl: "h-[40px] gap-[var(--space-2)] px-5 text-[15px] [&_svg]:size-[18px]",
237
+ default: buttonSizeMd,
238
+ /** @deprecated Use IconButton `size="lg"`. */
239
+ icon: "size-[36px] p-0 [&_svg]:size-[18px]",
240
+ /** @deprecated Use IconButton `size="md"`. */
241
+ "icon-xs": "size-[32px] p-0 [&_svg]:size-[14px] [&_svg]:stroke-[1.75]"
242
+ };
243
+ var buttonCva = cva(
191
244
  [
192
245
  "inline-flex cursor-pointer items-center justify-center whitespace-nowrap rounded-[var(--radius-md)] border font-medium",
193
246
  "transition-[background-color,border-color,color,box-shadow,transform] motion-reduce:transition-none",
@@ -199,42 +252,49 @@ var buttonVariants = cva(
199
252
  ].join(" "),
200
253
  {
201
254
  variants: {
255
+ // Legacy single axis, deprecated in 0.19 for `appearance` × `tone` and removed in 0.20.
256
+ // Frozen at 0.18; button-appearance.test.tsx keeps each value equal to its new pair.
202
257
  variant: {
258
+ /** @deprecated Use `appearance="filled" tone="primary"`. */
203
259
  primary: "border-[var(--primary)] bg-[var(--primary)] text-[var(--primary-fg)] hover:bg-[var(--primary-hover)] hover:border-[var(--primary-hover)]",
204
- /** @deprecated Use `secondary` — the single supporting-action family (#58). Removed in the next breaking release. */
260
+ /** @deprecated Use `appearance="outline" tone="neutral"`. */
205
261
  outline: "border-[var(--border)] bg-transparent text-foreground hover:border-[var(--border-strong)] hover:bg-[var(--action-hover)]",
262
+ /** @deprecated Use `appearance="ghost" tone="neutral"`. */
206
263
  ghost: "border-transparent bg-transparent text-text-secondary hover:bg-[var(--action-hover)] hover:text-foreground",
264
+ /** @deprecated Use `appearance="filled" tone="danger"`. */
207
265
  danger: "border-[var(--error)] bg-[var(--error)] text-[var(--error-contrast)] hover:bg-[var(--error-hover)] hover:border-[var(--error-hover)]",
266
+ /** @deprecated Use `appearance="filled" tone="danger"`. */
208
267
  emergency: "border-[var(--error)] bg-[var(--error)] text-[var(--error-contrast)] hover:bg-[var(--error-hover)] hover:border-[var(--error-hover)]",
209
- /** @deprecated Use `secondary` (identical styling, #58). Removed in the next breaking release. */
268
+ /** @deprecated Use `appearance="outline" tone="neutral"`. */
210
269
  subtle: "border-[var(--border-subtle)] bg-[var(--bg-surface)] text-foreground hover:border-[var(--border)] hover:bg-[var(--action-hover)]",
270
+ /** @deprecated Use `appearance="filled" tone="primary"`. */
211
271
  default: "border-[var(--primary)] bg-[var(--primary)] text-[var(--primary-fg)] hover:bg-[var(--primary-hover)] hover:border-[var(--primary-hover)]",
272
+ /** @deprecated Use `appearance="filled" tone="danger"`. */
212
273
  destructive: "border-[var(--error)] bg-[var(--error)] text-[var(--error-contrast)] hover:bg-[var(--error-hover)] hover:border-[var(--error-hover)]",
274
+ /** @deprecated Use `appearance="outline" tone="neutral"`. */
213
275
  secondary: "border-[var(--border-subtle)] bg-[var(--bg-surface)] text-foreground hover:border-[var(--border)] hover:bg-[var(--action-hover)]",
276
+ /** @deprecated No replacement on Button; a link component is planned. */
214
277
  link: "border-transparent bg-transparent px-0 text-[var(--accent)] underline-offset-4 hover:underline"
215
278
  },
216
- size: {
217
- // Cross-control height map: Button sm/md/lg match field controls sm/md/lg (30/32/36px).
218
- // Height comes from h-* alone — no vertical padding (py-12 inside h-32 left a 6px content box).
219
- // xs/sm: invisible ::before stretches the hit area to 44px tall (#69), vertically only so
220
- // side-by-side buttons never overlap. Its inset is measured from the padding box, inside
221
- // the 1px border every variant carries: inset = (44 - (height - 2 × 1px)) / 2.
222
- xs: "relative h-[28px] gap-[6px] px-pad-compact-x text-[12px] before:absolute before:inset-x-0 before:-inset-y-[9px] before:content-[''] [&_svg]:size-[14px] [&_svg]:stroke-[1.75]",
223
- sm: "relative h-[30px] gap-[6px] px-pad-compact-x text-body-sm before:absolute before:inset-x-0 before:-inset-y-[8px] before:content-[''] [&_svg]:size-[16px]",
224
- md: "h-[32px] gap-[6px] px-pad-default-x text-body-sm [&_svg]:size-[16px]",
225
- lg: "h-[36px] gap-[var(--space-2)] px-pad-default-x text-[15px] [&_svg]:size-[18px]",
226
- xl: "h-[40px] gap-[var(--space-2)] px-5 text-[15px] [&_svg]:size-[18px]",
227
- default: "h-[32px] gap-[6px] px-pad-default-x text-body-sm [&_svg]:size-[16px]",
228
- icon: "size-[36px] p-0 [&_svg]:size-[18px]",
229
- "icon-xs": "size-[32px] p-0 [&_svg]:size-[14px] [&_svg]:stroke-[1.75]"
230
- }
279
+ ...actionAxisVariants,
280
+ size: buttonSizes
231
281
  },
282
+ compoundVariants: [...actionCompoundVariants],
232
283
  defaultVariants: {
233
284
  variant: "primary",
234
285
  size: "md"
235
286
  }
236
287
  }
237
288
  );
289
+ function buttonVariants(options) {
290
+ if (options?.appearance == null && options?.tone == null) return buttonCva(options);
291
+ return buttonCva({
292
+ ...options,
293
+ ...resolveActionStyle(options.appearance, options.tone, "filled"),
294
+ variant: null
295
+ });
296
+ }
297
+ var buttonSizeClassName = (size) => buttonSizes[size];
238
298
  var footerButtonHitAreaClassName = "relative before:absolute before:inset-x-0 before:-inset-y-[5px] before:content-['']";
239
299
  var Button = React.forwardRef(
240
300
  ({
@@ -247,6 +307,8 @@ var Button = React.forwardRef(
247
307
  trailingIcon: TrailingIcon,
248
308
  type = "button",
249
309
  variant,
310
+ appearance,
311
+ tone,
250
312
  size,
251
313
  style,
252
314
  ...props
@@ -281,7 +343,7 @@ var Button = React.forwardRef(
281
343
  "aria-disabled": isDisabled ? true : void 0,
282
344
  disabled: !asChild ? isDisabled : void 0,
283
345
  type: !asChild ? type : void 0,
284
- className: cn(buttonVariants({ variant, size, className })),
346
+ className: cn(buttonVariants({ variant, appearance, tone, size, className })),
285
347
  style: frozenStyle ? { ...frozenStyle, ...style } : style,
286
348
  ...props,
287
349
  children: [
@@ -2963,7 +3025,7 @@ var tabsListVariants = cva8(
2963
3025
  {
2964
3026
  variant: "line",
2965
3027
  orientation: "vertical",
2966
- className: "border-r border-[var(--border-subtle)] gap-[var(--space-2)] pr-[var(--space-4)]"
3028
+ className: "border-r border-[var(--border-subtle)] gap-[var(--space-2)] pr-[var(--space-4)] max-sm:flex-row max-sm:gap-[var(--space-6)] max-sm:overflow-x-auto max-sm:overflow-y-hidden max-sm:border-r-0 max-sm:border-b max-sm:pr-0"
2967
3029
  },
2968
3030
  {
2969
3031
  variant: "contained",
@@ -3006,9 +3068,24 @@ var tabsTriggerVariants = cva8(
3006
3068
  },
3007
3069
  orientation: {
3008
3070
  horizontal: "",
3009
- vertical: "w-full justify-start data-[state=active]:after:bottom-0 data-[state=active]:after:left-auto data-[state=active]:after:right-[-1px] data-[state=active]:after:top-0 data-[state=active]:after:h-auto data-[state=active]:after:w-[2px]"
3071
+ vertical: "sm:w-full sm:justify-start sm:data-[state=active]:after:bottom-0 sm:data-[state=active]:after:left-auto sm:data-[state=active]:after:right-[calc(-1*(var(--space-4)+1px))] sm:data-[state=active]:after:top-0 sm:data-[state=active]:after:h-auto sm:data-[state=active]:after:w-[2px]"
3010
3072
  }
3011
3073
  },
3074
+ compoundVariants: [
3075
+ // 가로 line 목록은 overflow-y-hidden이라 바깥 ring이 잘린다. 2px 안쪽 여백을 두고 inset-ring으로 안쪽에 그린다.
3076
+ // ring-inset은 쓰지 않는다: 토큰 테마의 --color-inset(bg-inset) 때문에 ring 색이 배경색으로 덮인다.
3077
+ // 트리거 사이는 -mx로 보정해 텍스트 간격은 그대로 두고, 양 끝 트리거는 안쪽 여백을 유지해 ring이 목록 안에 남는다.
3078
+ {
3079
+ variant: "line",
3080
+ orientation: "horizontal",
3081
+ className: "mx-[-2px] px-[2px] first:ml-0 last:mr-0 focus-visible:ring-0 focus-visible:inset-ring-2 focus-visible:inset-ring-[var(--focus-ring)]"
3082
+ },
3083
+ {
3084
+ variant: "line",
3085
+ orientation: "vertical",
3086
+ className: "max-sm:mx-[-2px] max-sm:px-[2px] max-sm:first:ml-0 max-sm:last:mr-0 max-sm:focus-visible:ring-0 max-sm:focus-visible:inset-ring-2 max-sm:focus-visible:inset-ring-[var(--focus-ring)]"
3087
+ }
3088
+ ],
3012
3089
  defaultVariants: {
3013
3090
  variant: "line",
3014
3091
  orientation: "horizontal"
@@ -4254,7 +4331,7 @@ Chip.displayName = "Chip";
4254
4331
  import * as React24 from "react";
4255
4332
  import { cva as cva10 } from "class-variance-authority";
4256
4333
  import { jsx as jsx26, jsxs as jsxs25 } from "react/jsx-runtime";
4257
- var iconButtonVariants = cva10(
4334
+ var iconButtonCva = cva10(
4258
4335
  [
4259
4336
  "olun-icon-button relative inline-flex cursor-pointer shrink-0 items-center justify-center before:absolute before:content-['']",
4260
4337
  "rounded-[var(--radius-md)] border font-medium",
@@ -4265,12 +4342,21 @@ var iconButtonVariants = cva10(
4265
4342
  ].join(" "),
4266
4343
  {
4267
4344
  variants: {
4345
+ // Legacy single axis, deprecated in 0.19 for `appearance` × `tone` and removed in 0.20.
4346
+ // Frozen at 0.18; button-appearance.test.tsx keeps ghost and subtle equal to their new
4347
+ // pair. `outline` and `danger` differ from the pair they migrate to (outline + neutral =
4348
+ // Button secondary, filled + danger = solid red).
4268
4349
  variant: {
4350
+ /** @deprecated Use `appearance="ghost" tone="neutral"` (the default). */
4269
4351
  ghost: "border-transparent bg-transparent text-text-secondary hover:bg-[var(--action-hover)] hover:text-foreground",
4352
+ /** @deprecated Use `appearance="outline" tone="neutral"`. */
4270
4353
  outline: "border-[var(--border-subtle)] bg-[var(--bg-surface)] text-text-secondary hover:border-[var(--border-strong)] hover:bg-[var(--action-hover)] hover:text-foreground",
4354
+ /** @deprecated Use `appearance="filled" tone="neutral"`. */
4271
4355
  subtle: "border-transparent bg-[var(--bg-inset)] text-text-secondary hover:bg-[var(--action-hover)] hover:text-foreground",
4356
+ /** @deprecated Use `appearance="filled" tone="danger"` (solid red, like Button). */
4272
4357
  danger: "border-transparent bg-[var(--error-bg)] text-[var(--error)] hover:bg-[var(--action-hover)]"
4273
4358
  },
4359
+ ...actionAxisVariants,
4274
4360
  // Hit area = 44px on every size. The ::before is absolutely positioned, so its
4275
4361
  // inset is measured from the padding box — inside the 1px border every variant
4276
4362
  // carries (transparent or not). Inset = (44 - (size - 2 × 1px border)) / 2.
@@ -4285,6 +4371,7 @@ var iconButtonVariants = cva10(
4285
4371
  }
4286
4372
  },
4287
4373
  compoundVariants: [
4374
+ ...actionCompoundVariants,
4288
4375
  {
4289
4376
  pressed: true,
4290
4377
  variant: ["ghost", "outline", "subtle"],
@@ -4294,6 +4381,11 @@ var iconButtonVariants = cva10(
4294
4381
  pressed: true,
4295
4382
  variant: "danger",
4296
4383
  className: "border-[var(--error)] bg-[var(--error-bg)] text-[var(--error)]"
4384
+ },
4385
+ {
4386
+ pressed: true,
4387
+ tone: "neutral",
4388
+ className: "border-[var(--border-strong)] bg-[var(--action-selected)] text-foreground"
4297
4389
  }
4298
4390
  ],
4299
4391
  // ghost is the intended default, not a fallback: ghost is reserved for icon-only
@@ -4306,6 +4398,33 @@ var iconButtonVariants = cva10(
4306
4398
  }
4307
4399
  }
4308
4400
  );
4401
+ var axisSizeClassName = {
4402
+ xs: "size-[28px] before:-inset-[9px]",
4403
+ sm: "size-[30px] before:-inset-[8px]",
4404
+ md: "size-[32px] before:-inset-[7px]",
4405
+ lg: "size-[36px] before:-inset-[5px]",
4406
+ xl: "size-[40px] before:-inset-[3px]"
4407
+ };
4408
+ var axisIcon = {
4409
+ xs: { size: 14, strokeWidth: 1.75 },
4410
+ sm: { size: 16, strokeWidth: 2 },
4411
+ md: { size: 16, strokeWidth: 2 },
4412
+ lg: { size: 18, strokeWidth: 2 },
4413
+ xl: { size: 18, strokeWidth: 2 }
4414
+ };
4415
+ function iconButtonVariants(options) {
4416
+ if (options?.appearance == null && options?.tone == null) {
4417
+ return iconButtonCva(options);
4418
+ }
4419
+ const { size, ...rest } = options;
4420
+ const paint = iconButtonCva({
4421
+ ...rest,
4422
+ ...resolveActionStyle(options.appearance, options.tone, "ghost"),
4423
+ variant: null,
4424
+ size: null
4425
+ });
4426
+ return size === null ? paint : `${paint} ${axisSizeClassName[size ?? "md"]}`;
4427
+ }
4309
4428
  var iconButtonBadgeVariants = cva10(
4310
4429
  [
4311
4430
  "pointer-events-none absolute right-0 top-0 inline-flex",
@@ -4332,6 +4451,11 @@ var iconSize2 = {
4332
4451
  md: 16,
4333
4452
  lg: 18
4334
4453
  };
4454
+ var textButtonSize = {
4455
+ sm: "xs",
4456
+ md: "md",
4457
+ lg: "xl"
4458
+ };
4335
4459
  var iconStrokeWidth = {
4336
4460
  sm: 2.25,
4337
4461
  md: 2.25,
@@ -4341,6 +4465,8 @@ var IconButton = React24.forwardRef(
4341
4465
  ({
4342
4466
  className,
4343
4467
  variant = "ghost",
4468
+ appearance,
4469
+ tone,
4344
4470
  size = "md",
4345
4471
  pressed = void 0,
4346
4472
  icon: Icon,
@@ -4350,39 +4476,63 @@ var IconButton = React24.forwardRef(
4350
4476
  title,
4351
4477
  "aria-label": ariaLabel,
4352
4478
  "aria-labelledby": ariaLabelledBy,
4479
+ iconPosition = "start",
4480
+ label,
4481
+ children,
4353
4482
  ...props
4354
4483
  }, ref) => {
4355
4484
  const resolvedSize = size ?? "md";
4485
+ const usesAxes = appearance != null || tone != null;
4356
4486
  const isToggle = pressed !== void 0;
4357
4487
  const isPressed = pressed === true;
4358
4488
  const hoverTitle = title ?? (typeof ariaLabel === "string" ? ariaLabel : void 0);
4489
+ const text = label ?? children;
4490
+ const hasText = text !== void 0 && text !== null && text !== false;
4491
+ const style = usesAxes ? resolveActionStyle(appearance, tone, "ghost") : void 0;
4492
+ const axisSize = usesAxes ? resolvedSize : void 0;
4493
+ const legacySize = resolvedSize;
4494
+ const textBoxSize = axisSize ?? textButtonSize[legacySize];
4495
+ const icon = /* @__PURE__ */ jsx26(
4496
+ Icon,
4497
+ {
4498
+ "aria-hidden": "true",
4499
+ "data-slot": "icon-button-icon",
4500
+ size: axisSize ? axisIcon[axisSize].size : iconSize2[legacySize],
4501
+ strokeWidth: axisSize ? axisIcon[axisSize].strokeWidth : hasText ? void 0 : iconStrokeWidth[legacySize]
4502
+ }
4503
+ );
4359
4504
  return /* @__PURE__ */ jsxs25(
4360
4505
  "button",
4361
4506
  {
4362
4507
  ref,
4363
4508
  ...props,
4364
4509
  "data-slot": "icon-button",
4365
- "data-variant": variant,
4510
+ "data-variant": usesAxes ? void 0 : variant,
4511
+ "data-appearance": style?.appearance,
4512
+ "data-tone": style?.tone,
4366
4513
  "data-size": resolvedSize,
4514
+ "data-icon-position": hasText ? iconPosition : void 0,
4367
4515
  "data-pressed": isToggle ? String(isPressed) : void 0,
4368
4516
  "aria-label": ariaLabel,
4369
4517
  "aria-labelledby": ariaLabelledBy,
4370
4518
  "aria-pressed": isToggle ? isPressed : void 0,
4371
4519
  title: hoverTitle,
4372
4520
  className: cn(
4373
- iconButtonVariants({ variant, size, pressed: isPressed, className })
4521
+ iconButtonVariants({
4522
+ variant,
4523
+ appearance,
4524
+ tone,
4525
+ size: hasText ? null : size,
4526
+ pressed: isPressed
4527
+ }),
4528
+ hasText && ["whitespace-nowrap", buttonSizeClassName(textBoxSize)],
4529
+ className
4374
4530
  ),
4375
4531
  type: type ?? "button",
4376
4532
  children: [
4377
- /* @__PURE__ */ jsx26(
4378
- Icon,
4379
- {
4380
- "aria-hidden": "true",
4381
- "data-slot": "icon-button-icon",
4382
- size: iconSize2[resolvedSize],
4383
- strokeWidth: iconStrokeWidth[resolvedSize]
4384
- }
4385
- ),
4533
+ hasText && iconPosition === "end" ? null : icon,
4534
+ hasText ? text : null,
4535
+ hasText && iconPosition === "end" ? icon : null,
4386
4536
  badge !== void 0 ? /* @__PURE__ */ jsx26(
4387
4537
  "span",
4388
4538
  {
@@ -9722,7 +9872,10 @@ function TreeView({
9722
9872
 
9723
9873
  // src/components/branding/olun-logo.tsx
9724
9874
  import { jsx as jsx58, jsxs as jsxs51 } from "react/jsx-runtime";
9875
+ var SYMBOL_LARGE = { stroke: 24, dotX: 184, dotY: 180, dotR: 13 };
9876
+ var SYMBOL_SMALL = { stroke: 18, dotX: 185, dotY: 175, dotR: 22 };
9725
9877
  function OlunSymbol({ size = 28, className }) {
9878
+ const g = size > 32 ? SYMBOL_LARGE : SYMBOL_SMALL;
9726
9879
  return /* @__PURE__ */ jsxs51(
9727
9880
  "svg",
9728
9881
  {
@@ -9741,12 +9894,12 @@ function OlunSymbol({ size = 28, className }) {
9741
9894
  cy: "120",
9742
9895
  r: "60",
9743
9896
  stroke: "currentColor",
9744
- strokeWidth: "18",
9897
+ strokeWidth: g.stroke,
9745
9898
  fill: "none",
9746
9899
  style: { color: "var(--text-primary)" }
9747
9900
  }
9748
9901
  ),
9749
- /* @__PURE__ */ jsx58("circle", { cx: "185", cy: "175", r: "22", fill: "var(--accent-pop)" })
9902
+ /* @__PURE__ */ jsx58("circle", { cx: g.dotX, cy: g.dotY, r: g.dotR, fill: "var(--accent-pop)" })
9750
9903
  ]
9751
9904
  }
9752
9905
  );
@@ -9780,7 +9933,7 @@ function OlunLogo({
9780
9933
  },
9781
9934
  children: [
9782
9935
  "OLUN",
9783
- /* @__PURE__ */ jsx58("span", { style: { color: "var(--accent)" }, children: "." })
9936
+ !showSymbol && /* @__PURE__ */ jsx58("span", { style: { color: "var(--accent)" }, children: "." })
9784
9937
  ]
9785
9938
  }
9786
9939
  )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olundot/ui",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "OLUN. shared React UI components built on OLUN design tokens",
5
5
  "license": "MIT",
6
6
  "keywords": [
@@ -44,7 +44,7 @@
44
44
  "lucide-react": "^1.8.0",
45
45
  "radix-ui": "^1.4.3",
46
46
  "tailwind-merge": "^3.5.0",
47
- "@olundot/tokens": "^0.16.0"
47
+ "@olundot/tokens": "^0.17.0"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "react": ">=18",