sh-ui-cli 0.49.0 → 0.50.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 (85) hide show
  1. package/data/changelog/versions.json +14 -0
  2. package/data/registry/react/components/accordion/index.vanilla-extract.tsx +97 -0
  3. package/data/registry/react/components/accordion/styles.css.ts +131 -0
  4. package/data/registry/react/components/avatar/index.vanilla-extract.tsx +73 -0
  5. package/data/registry/react/components/avatar/styles.css.ts +68 -0
  6. package/data/registry/react/components/badge/index.vanilla-extract.tsx +40 -0
  7. package/data/registry/react/components/badge/styles.css.ts +71 -0
  8. package/data/registry/react/components/breadcrumb/index.vanilla-extract.tsx +152 -0
  9. package/data/registry/react/components/breadcrumb/styles.css.ts +95 -0
  10. package/data/registry/react/components/calendar/index.vanilla-extract.tsx +806 -0
  11. package/data/registry/react/components/calendar/styles.css.ts +250 -0
  12. package/data/registry/react/components/carousel/index.vanilla-extract.tsx +430 -0
  13. package/data/registry/react/components/carousel/styles.css.ts +169 -0
  14. package/data/registry/react/components/checkbox/index.vanilla-extract.tsx +96 -0
  15. package/data/registry/react/components/checkbox/styles.css.ts +74 -0
  16. package/data/registry/react/components/code-editor/index.vanilla-extract.tsx +230 -0
  17. package/data/registry/react/components/code-editor/styles.css.ts +97 -0
  18. package/data/registry/react/components/code-panel/index.vanilla-extract.tsx +191 -0
  19. package/data/registry/react/components/code-panel/styles.css.ts +151 -0
  20. package/data/registry/react/components/color-picker/index.vanilla-extract.tsx +467 -0
  21. package/data/registry/react/components/color-picker/styles.css.ts +169 -0
  22. package/data/registry/react/components/combobox/index.vanilla-extract.tsx +165 -0
  23. package/data/registry/react/components/combobox/styles.css.ts +174 -0
  24. package/data/registry/react/components/context-menu/index.vanilla-extract.tsx +251 -0
  25. package/data/registry/react/components/context-menu/styles.css.ts +167 -0
  26. package/data/registry/react/components/date-picker/index.vanilla-extract.tsx +520 -0
  27. package/data/registry/react/components/date-picker/styles.css.ts +111 -0
  28. package/data/registry/react/components/dialog/index.vanilla-extract.tsx +95 -0
  29. package/data/registry/react/components/dialog/styles.css.ts +140 -0
  30. package/data/registry/react/components/dropdown-menu/index.vanilla-extract.tsx +255 -0
  31. package/data/registry/react/components/dropdown-menu/styles.css.ts +175 -0
  32. package/data/registry/react/components/file-upload/index.vanilla-extract.tsx +487 -0
  33. package/data/registry/react/components/file-upload/styles.css.ts +193 -0
  34. package/data/registry/react/components/form/index.vanilla-extract.tsx +61 -0
  35. package/data/registry/react/components/form/styles.css.ts +56 -0
  36. package/data/registry/react/components/header/index.vanilla-extract.tsx +805 -0
  37. package/data/registry/react/components/header/styles.css.ts +413 -0
  38. package/data/registry/react/components/label/index.vanilla-extract.tsx +52 -0
  39. package/data/registry/react/components/label/styles.css.ts +141 -0
  40. package/data/registry/react/components/markdown-editor/index.vanilla-extract.tsx +119 -0
  41. package/data/registry/react/components/markdown-editor/styles.css.ts +231 -0
  42. package/data/registry/react/components/menubar/index.vanilla-extract.tsx +32 -0
  43. package/data/registry/react/components/menubar/styles.css.ts +53 -0
  44. package/data/registry/react/components/numeric-input/index.vanilla-extract.tsx +148 -0
  45. package/data/registry/react/components/numeric-input/styles.css.ts +65 -0
  46. package/data/registry/react/components/page-toc/index.vanilla-extract.tsx +174 -0
  47. package/data/registry/react/components/page-toc/styles.css.ts +97 -0
  48. package/data/registry/react/components/pagination/index.vanilla-extract.tsx +269 -0
  49. package/data/registry/react/components/pagination/styles.css.ts +113 -0
  50. package/data/registry/react/components/popover/index.vanilla-extract.tsx +113 -0
  51. package/data/registry/react/components/popover/styles.css.ts +78 -0
  52. package/data/registry/react/components/progress/index.vanilla-extract.tsx +54 -0
  53. package/data/registry/react/components/progress/styles.css.ts +53 -0
  54. package/data/registry/react/components/radio/index.vanilla-extract.tsx +65 -0
  55. package/data/registry/react/components/radio/styles.css.ts +79 -0
  56. package/data/registry/react/components/rich-text-editor/index.vanilla-extract.tsx +348 -0
  57. package/data/registry/react/components/rich-text-editor/styles.css.ts +243 -0
  58. package/data/registry/react/components/select/index.vanilla-extract.tsx +234 -0
  59. package/data/registry/react/components/select/styles.css.ts +225 -0
  60. package/data/registry/react/components/separator/index.vanilla-extract.tsx +46 -0
  61. package/data/registry/react/components/separator/styles.css.ts +24 -0
  62. package/data/registry/react/components/sidebar/index.vanilla-extract.tsx +1067 -0
  63. package/data/registry/react/components/sidebar/styles.css.ts +578 -0
  64. package/data/registry/react/components/skeleton/index.vanilla-extract.tsx +22 -0
  65. package/data/registry/react/components/skeleton/styles.css.ts +30 -0
  66. package/data/registry/react/components/slider/index.vanilla-extract.tsx +298 -0
  67. package/data/registry/react/components/slider/styles.css.ts +75 -0
  68. package/data/registry/react/components/spinner/index.vanilla-extract.tsx +38 -0
  69. package/data/registry/react/components/spinner/styles.css.ts +60 -0
  70. package/data/registry/react/components/switch/index.vanilla-extract.tsx +39 -0
  71. package/data/registry/react/components/switch/styles.css.ts +87 -0
  72. package/data/registry/react/components/tabs/index.vanilla-extract.tsx +91 -0
  73. package/data/registry/react/components/tabs/styles.css.ts +145 -0
  74. package/data/registry/react/components/textarea/index.vanilla-extract.tsx +23 -0
  75. package/data/registry/react/components/textarea/styles.css.ts +55 -0
  76. package/data/registry/react/components/toast/index.vanilla-extract.tsx +258 -0
  77. package/data/registry/react/components/toast/styles.css.ts +307 -0
  78. package/data/registry/react/components/toggle/index.vanilla-extract.tsx +131 -0
  79. package/data/registry/react/components/toggle/styles.css.ts +109 -0
  80. package/data/registry/react/components/tooltip/index.vanilla-extract.tsx +83 -0
  81. package/data/registry/react/components/tooltip/styles.css.ts +59 -0
  82. package/data/registry/react/registry.json +853 -36
  83. package/package.json +1 -1
  84. package/src/api.d.ts +4 -3
  85. package/src/constants.js +4 -3
@@ -0,0 +1,413 @@
1
+ import { style } from "@vanilla-extract/css";
2
+
3
+ export const header = style({
4
+ position: "relative",
5
+ display: "flex",
6
+ alignItems: "center",
7
+ gap: "var(--space-4)",
8
+ height: "var(--control-md)",
9
+ padding: "0 var(--space-3)",
10
+ background: "var(--background)",
11
+ borderBottom: "1px solid var(--border)",
12
+ transition: "transform var(--duration-base) var(--ease-standard),\n background-color var(--duration-base) var(--ease-standard)",
13
+ "--sh-ui-header-hover-bg": "var(--background-muted)",
14
+ "--sh-ui-header-blur-opacity": "85%",
15
+ "--sh-ui-header-blur-radius": "16px",
16
+ selectors: {
17
+ "&[data-sticky-hide][data-hidden]": {
18
+ transform: "translateY(-100%)",
19
+ },
20
+ },
21
+ "@media": {
22
+ "(prefers-reduced-motion: reduce)": {
23
+ transition: "none",
24
+ },
25
+ "(max-width: 767px)": {
26
+ gap: "var(--space-2)",
27
+ },
28
+ },
29
+ });
30
+
31
+ export const headerSolid = style({
32
+ background: "var(--background)",
33
+ });
34
+
35
+ export const headerTransparent = style({
36
+ background: "transparent",
37
+ borderBottomColor: "transparent",
38
+ "--sh-ui-header-hover-bg": "color-mix(in srgb, currentColor 14%, transparent)",
39
+ });
40
+
41
+ export const headerBlur = style({
42
+ background: "color-mix(in srgb, var(--background) var(--sh-ui-header-blur-opacity), transparent)",
43
+ backdropFilter: "saturate(180%) blur(var(--sh-ui-header-blur-radius))",
44
+ WebkitBackdropFilter: "saturate(180%) blur(var(--sh-ui-header-blur-radius))",
45
+ "--sh-ui-header-hover-bg": "color-mix(in srgb, currentColor 14%, transparent)",
46
+ });
47
+
48
+ export const header__brand = style({
49
+ display: "inline-flex",
50
+ alignItems: "center",
51
+ gap: "var(--space-2)",
52
+ flexShrink: 0,
53
+ });
54
+
55
+ export const header__logo = style({
56
+ display: "inline-flex",
57
+ alignItems: "center",
58
+ color: "var(--foreground)",
59
+ });
60
+
61
+ export const header__title = style({
62
+ fontSize: "var(--text-base)",
63
+ fontWeight: "var(--weight-bold)",
64
+ color: "var(--foreground)",
65
+ letterSpacing: "-0.3px",
66
+ });
67
+
68
+ export const header__trigger = style({
69
+ display: "none",
70
+ alignItems: "center",
71
+ justifyContent: "center",
72
+ width: "2.25rem",
73
+ height: "2.25rem",
74
+ padding: 0,
75
+ background: "transparent",
76
+ border: 0,
77
+ color: "var(--foreground)",
78
+ borderRadius: "calc(var(--radius) - 2px)",
79
+ cursor: "pointer",
80
+ transition: "background-color var(--duration-fast)",
81
+ selectors: {
82
+ "&:hover": {
83
+ background: "var(--sh-ui-header-hover-bg)",
84
+ },
85
+ "&:focus-visible": {
86
+ outline: "var(--border-width-strong) solid var(--foreground)",
87
+ outlineOffset: "2px",
88
+ },
89
+ },
90
+ "@media": {
91
+ "(max-width: 767px)": {
92
+ display: "inline-flex",
93
+ order: -1,
94
+ },
95
+ },
96
+ });
97
+
98
+ export const header__nav = style({
99
+ display: "flex",
100
+ alignItems: "center",
101
+ gap: "var(--space-1)",
102
+ flex: 1,
103
+ minWidth: 0,
104
+ overflowX: "auto",
105
+ scrollbarWidth: "none",
106
+ selectors: {
107
+ "&::-webkit-scrollbar": {
108
+ display: "none",
109
+ },
110
+ },
111
+ "@media": {
112
+ "(max-width: 767px)": {
113
+ display: "none",
114
+ },
115
+ },
116
+ });
117
+
118
+ export const header__item = style({
119
+ display: "inline-flex",
120
+ alignItems: "center",
121
+ gap: "var(--space-1)",
122
+ padding: "var(--space-2) var(--space-3)",
123
+ fontSize: "var(--text-sm)",
124
+ fontWeight: "var(--weight-medium)",
125
+ color: "var(--foreground-muted)",
126
+ textDecoration: "none",
127
+ background: "transparent",
128
+ border: 0,
129
+ borderRadius: "calc(var(--radius) - 2px)",
130
+ cursor: "pointer",
131
+ whiteSpace: "nowrap",
132
+ transition: "color var(--duration-fast), background-color var(--duration-fast)",
133
+ selectors: {
134
+ "&:hover": {
135
+ color: "var(--foreground)",
136
+ background: "var(--sh-ui-header-hover-bg)",
137
+ },
138
+ "&[data-active]": {
139
+ color: "var(--foreground)",
140
+ fontWeight: "var(--weight-semibold)",
141
+ },
142
+ "&:focus-visible": {
143
+ outline: "var(--border-width-strong) solid var(--foreground)",
144
+ outlineOffset: "2px",
145
+ },
146
+ },
147
+ });
148
+
149
+ export const header__actions = style({
150
+ display: "inline-flex",
151
+ alignItems: "center",
152
+ gap: "var(--space-2)",
153
+ marginLeft: "auto",
154
+ flexShrink: 0,
155
+ });
156
+
157
+ export const headerDesktopOnly = style({
158
+ display: "contents",
159
+ "@media": {
160
+ "(max-width: 767px)": {
161
+ display: "none",
162
+ },
163
+ },
164
+ });
165
+
166
+ export const headerMobileOnly = style({
167
+ display: "none",
168
+ "@media": {
169
+ "(max-width: 767px)": {
170
+ display: "contents",
171
+ },
172
+ },
173
+ });
174
+
175
+ export const headerGroupInline = style({
176
+ display: "contents",
177
+ });
178
+
179
+ export const headerGroupDrawer = style({
180
+ display: "flex",
181
+ flexDirection: "column",
182
+ marginTop: "var(--space-3)",
183
+ selectors: {
184
+ "&:first-child": {
185
+ marginTop: 0,
186
+ },
187
+ },
188
+ });
189
+
190
+ export const headerGroupLabel = style({
191
+ display: "flex",
192
+ alignItems: "center",
193
+ height: "2rem",
194
+ padding: "0 var(--space-2)",
195
+ fontSize: "var(--text-xs)",
196
+ fontWeight: "var(--weight-medium)",
197
+ color: "var(--foreground-muted)",
198
+ });
199
+
200
+ export const headerGroupItems = style({
201
+ display: "flex",
202
+ flexDirection: "column",
203
+ gap: "1px",
204
+ });
205
+
206
+ export const header__menu = style({
207
+ position: "relative",
208
+ });
209
+
210
+ export const headerMenuInline = style({
211
+ display: "inline-block",
212
+ });
213
+
214
+ export const headerMenuTrigger = style({
215
+ display: "inline-flex",
216
+ alignItems: "center",
217
+ gap: "var(--space-1)",
218
+ padding: "var(--space-2) var(--space-3)",
219
+ fontSize: "var(--text-sm)",
220
+ fontWeight: "var(--weight-medium)",
221
+ color: "var(--foreground-muted)",
222
+ background: "transparent",
223
+ border: 0,
224
+ borderRadius: "calc(var(--radius) - 2px)",
225
+ cursor: "pointer",
226
+ whiteSpace: "nowrap",
227
+ transition: "color var(--duration-fast), background-color var(--duration-fast)",
228
+ selectors: {
229
+ "&:hover": {
230
+ color: "var(--foreground)",
231
+ background: "var(--sh-ui-header-hover-bg)",
232
+ },
233
+ "&[data-open]": {
234
+ color: "var(--foreground)",
235
+ background: "var(--sh-ui-header-hover-bg)",
236
+ },
237
+ "&:focus-visible": {
238
+ outline: "var(--border-width-strong) solid var(--foreground)",
239
+ outlineOffset: "2px",
240
+ },
241
+ [`&[data-open] ${header__chevron}`]: {
242
+ transform: "rotate(180deg)",
243
+ },
244
+ },
245
+ });
246
+
247
+ export const header__chevron = style({
248
+ transition: "transform var(--duration-fast) var(--ease-standard)",
249
+ });
250
+
251
+ export const headerMenuContentPortal = style({
252
+ zIndex: "var(--z-dropdown, 50)",
253
+ padding: "var(--space-1)",
254
+ background: "var(--background)",
255
+ border: "1px solid var(--border)",
256
+ borderRadius: "var(--radius)",
257
+ boxShadow: "0 8px 24px -8px rgba(0, 0, 0, 0.18)",
258
+ display: "flex",
259
+ flexDirection: "column",
260
+ gap: "1px",
261
+ color: "var(--foreground)",
262
+ selectors: {
263
+ [`& ${header__item}`]: {
264
+ padding: "var(--space-2) var(--space-3)",
265
+ fontSize: "var(--text-sm)",
266
+ },
267
+ },
268
+ });
269
+
270
+ export const header__backdrop = style({
271
+ display: "none",
272
+ "@media": {
273
+ "(max-width: 767px)": {
274
+ display: "block",
275
+ position: "fixed",
276
+ inset: 0,
277
+ background: "rgba(0, 0, 0, 0.25)",
278
+ backdropFilter: "blur(8px)",
279
+ zIndex: "var(--z-overlay)",
280
+ opacity: 0,
281
+ pointerEvents: "none",
282
+ transition: "opacity var(--duration-base) var(--ease-standard)",
283
+ selectors: {
284
+ "&[data-open]": {
285
+ opacity: 1,
286
+ pointerEvents: "auto",
287
+ },
288
+ },
289
+ },
290
+ },
291
+ });
292
+
293
+ export const header__drawer = style({
294
+ display: "none",
295
+ "@media": {
296
+ "(max-width: 767px)": {
297
+ display: "flex",
298
+ position: "fixed",
299
+ left: 0,
300
+ top: 0,
301
+ bottom: 0,
302
+ width: "min(17.5rem, 85vw)",
303
+ background: "var(--background-subtle)",
304
+ borderRight: "1px solid var(--border)",
305
+ zIndex: "var(--z-modal)",
306
+ transform: "translateX(-100%)",
307
+ transition: "transform var(--duration-base) var(--ease-standard)",
308
+ flexDirection: "column",
309
+ overflowY: "auto",
310
+ selectors: {
311
+ "&[data-open]": {
312
+ transform: "translateX(0)",
313
+ },
314
+ [`& ${header__item}`]: {
315
+ padding: "var(--space-3) var(--space-3)",
316
+ fontSize: "var(--text-sm)",
317
+ borderRadius: "calc(var(--radius) - 2px)",
318
+ },
319
+ },
320
+ },
321
+ },
322
+ });
323
+
324
+ export const headerDrawerHead = style({
325
+ "@media": {
326
+ "(max-width: 767px)": {
327
+ display: "flex",
328
+ alignItems: "center",
329
+ justifyContent: "flex-end",
330
+ padding: "var(--space-2) var(--space-2)",
331
+ borderBottom: "1px solid var(--border)",
332
+ },
333
+ },
334
+ });
335
+
336
+ export const headerDrawerNav = style({
337
+ "@media": {
338
+ "(max-width: 767px)": {
339
+ display: "flex",
340
+ flexDirection: "column",
341
+ padding: "var(--space-2)",
342
+ gap: "1px",
343
+ },
344
+ },
345
+ });
346
+
347
+ export const headerMenuDrawer = style({
348
+ "@media": {
349
+ "(max-width: 767px)": {
350
+ display: "flex",
351
+ flexDirection: "column",
352
+ selectors: {
353
+ [`& > ${headerMenuTrigger}`]: {
354
+ justifyContent: "space-between",
355
+ width: "100%",
356
+ padding: "var(--space-3) var(--space-3)",
357
+ },
358
+ [`& > ${headerMenuContent}`]: {
359
+ display: "flex",
360
+ flexDirection: "column",
361
+ padding: "var(--space-1) 0 var(--space-1) var(--space-4)",
362
+ gap: "1px",
363
+ },
364
+ [`& > ${headerMenuContent}[hidden]`]: {
365
+ display: "none",
366
+ },
367
+ },
368
+ },
369
+ },
370
+ });
371
+
372
+ export const header__group = style({
373
+ });
374
+
375
+ export const headerMenuTriggerLabel = style({
376
+ });
377
+
378
+ export const headerMenuContent = style({
379
+ });
380
+
381
+ /** 동적 키로 클래스 참조용 — `byKey[\`badge--${variant}\`]` 같은 패턴 지원. */
382
+ export const byKey: Record<string, string> = {
383
+ "header": header,
384
+ "header--solid": headerSolid,
385
+ "header--transparent": headerTransparent,
386
+ "header--blur": headerBlur,
387
+ "header__brand": header__brand,
388
+ "header__logo": header__logo,
389
+ "header__title": header__title,
390
+ "header__trigger": header__trigger,
391
+ "header__nav": header__nav,
392
+ "header__item": header__item,
393
+ "header__actions": header__actions,
394
+ "header__desktop-only": headerDesktopOnly,
395
+ "header__mobile-only": headerMobileOnly,
396
+ "header__group--inline": headerGroupInline,
397
+ "header__group--drawer": headerGroupDrawer,
398
+ "header__group-label": headerGroupLabel,
399
+ "header__group-items": headerGroupItems,
400
+ "header__menu": header__menu,
401
+ "header__menu--inline": headerMenuInline,
402
+ "header__menu-trigger": headerMenuTrigger,
403
+ "header__chevron": header__chevron,
404
+ "header__menu-content--portal": headerMenuContentPortal,
405
+ "header__backdrop": header__backdrop,
406
+ "header__drawer": header__drawer,
407
+ "header__drawer-head": headerDrawerHead,
408
+ "header__drawer-nav": headerDrawerNav,
409
+ "header__menu--drawer": headerMenuDrawer,
410
+ "header__group": header__group,
411
+ "header__menu-trigger-label": headerMenuTriggerLabel,
412
+ "header__menu-content": headerMenuContent,
413
+ };
@@ -0,0 +1,52 @@
1
+ import * as React from "react";
2
+ import { byKey, label, label__subtitle, label__description, label__caption, label__title } from "./styles.css";
3
+
4
+ import { cn } from "@SH_UI_UTILS@";
5
+ export interface LabelProps extends React.LabelHTMLAttributes<HTMLLabelElement> {
6
+ /**
7
+ * 필수 필드 표시. `true`면 `::after`로 `*` 표시가 붙는다.
8
+ * Input에 `required` 속성이 있으면 CSS `:has()`로 자동 감지되므로 보통 명시 불필요.
9
+ *
10
+ * @default false
11
+ */
12
+ isRequired?: boolean;
13
+ }
14
+
15
+
16
+ /**
17
+ * 폼 컨트롤과 1:1로 연결되는 레이블. `htmlFor`로 컨트롤의 `id`와 매칭하거나
18
+ * Label 안에 컨트롤을 감싸 묵시적으로 연결한다. 클릭 시 자동으로 컨트롤에 포커스가 간다.
19
+ */
20
+ export const Label = React.forwardRef<HTMLLabelElement, LabelProps>(
21
+ ({ className, children, isRequired, ...props }, ref) => (
22
+ <label
23
+ ref={ref}
24
+ className={cn(label, className)}
25
+ data-required={isRequired || undefined}
26
+ {...props}
27
+ >
28
+ {children}
29
+ </label>
30
+ ),
31
+ );
32
+ Label.displayName = "Label";
33
+
34
+ /** Label 안의 주 라벨 텍스트. 구조적 그룹핑이 필요할 때 Label과 함께 사용. */
35
+ export function LabelTitle({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) {
36
+ return <span className={cn(label__title, className)} {...props} />;
37
+ }
38
+
39
+ /** 라벨 옆에 약하게 표시되는 보조 텍스트(예: "선택 사항"). */
40
+ export function LabelSubtitle({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) {
41
+ return <span className={cn(label__subtitle, className)} {...props} />;
42
+ }
43
+
44
+ /** 라벨 아래에 붙는 안내 문구. 컨트롤과 `aria-describedby`로 연결할 것. */
45
+ export function LabelDescription({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
46
+ return <p className={cn(label__description, className)} {...props} />;
47
+ }
48
+
49
+ /** 라벨 아래의 보조 캡션(예: 입력 형식 예시, 글자 수 제한). */
50
+ export function LabelCaption({ className, ...props }: React.HTMLAttributes<HTMLParagraphElement>) {
51
+ return <p className={cn(label__caption, className)} {...props} />;
52
+ }
@@ -0,0 +1,141 @@
1
+ import { style } from "@vanilla-extract/css";
2
+
3
+ export const label = style({
4
+ display: "flex",
5
+ flexDirection: "column",
6
+ gap: "0.125rem",
7
+ fontSize: "var(--text-sm)",
8
+ fontWeight: "var(--weight-medium)",
9
+ lineHeight: 1.4,
10
+ color: "var(--foreground)",
11
+ cursor: "pointer",
12
+ userSelect: "none",
13
+ selectors: {
14
+ [`&:not(:has(${label__title}`]: {
15
+ display: "block",
16
+ },
17
+ [`&[data-required] > ${label__title}::after`]: {
18
+ content: "\" *\"",
19
+ color: "var(--danger)",
20
+ fontWeight: "var(--weight-semibold)",
21
+ },
22
+ [`&:has(+ .input:required) > ${label__title}::after`]: {
23
+ content: "\" *\"",
24
+ color: "var(--danger)",
25
+ fontWeight: "var(--weight-semibold)",
26
+ },
27
+ [`&:has(+ .input-wrap .input:required) > ${label__title}::after`]: {
28
+ content: "\" *\"",
29
+ color: "var(--danger)",
30
+ fontWeight: "var(--weight-semibold)",
31
+ },
32
+ [`&:has(+ .textarea:required) > ${label__title}::after`]: {
33
+ content: "\" *\"",
34
+ color: "var(--danger)",
35
+ fontWeight: "var(--weight-semibold)",
36
+ },
37
+ [`&:has(+ .combobox__input:required) > ${label__title}::after`]: {
38
+ content: "\" *\"",
39
+ color: "var(--danger)",
40
+ fontWeight: "var(--weight-semibold)",
41
+ },
42
+ [`&[data-required]:not(:has(${label__title}))::after`]: {
43
+ content: "\" *\"",
44
+ color: "var(--danger)",
45
+ fontWeight: "var(--weight-semibold)",
46
+ },
47
+ [`&:not(:has(${label__title})):has(+ .input:required)::after`]: {
48
+ content: "\" *\"",
49
+ color: "var(--danger)",
50
+ fontWeight: "var(--weight-semibold)",
51
+ },
52
+ [`&:not(:has(${label__title})):has(+ .input-wrap .input:required)::after`]: {
53
+ content: "\" *\"",
54
+ color: "var(--danger)",
55
+ fontWeight: "var(--weight-semibold)",
56
+ },
57
+ [`&:not(:has(${label__title})):has(+ .textarea:required)::after`]: {
58
+ content: "\" *\"",
59
+ color: "var(--danger)",
60
+ fontWeight: "var(--weight-semibold)",
61
+ },
62
+ [`&:not(:has(${label__title})):has(+ .combobox__input:required)::after`]: {
63
+ content: "\" *\"",
64
+ color: "var(--danger)",
65
+ fontWeight: "var(--weight-semibold)",
66
+ },
67
+ "&:has(+ .input:disabled)": {
68
+ opacity: "var(--opacity-disabled)",
69
+ cursor: "not-allowed",
70
+ },
71
+ "&:has(+ .input-wrap .input:disabled)": {
72
+ opacity: "var(--opacity-disabled)",
73
+ cursor: "not-allowed",
74
+ },
75
+ "&:has(+ .textarea:disabled)": {
76
+ opacity: "var(--opacity-disabled)",
77
+ cursor: "not-allowed",
78
+ },
79
+ "&:has(+ .select__trigger:disabled)": {
80
+ opacity: "var(--opacity-disabled)",
81
+ cursor: "not-allowed",
82
+ },
83
+ "&:has(+ .combobox__input:disabled)": {
84
+ opacity: "var(--opacity-disabled)",
85
+ cursor: "not-allowed",
86
+ },
87
+ "&:has(+ .date-picker__trigger:disabled)": {
88
+ opacity: "var(--opacity-disabled)",
89
+ cursor: "not-allowed",
90
+ },
91
+ "&:has(+ .file-upload .file-upload__dropzone--disabled)": {
92
+ opacity: "var(--opacity-disabled)",
93
+ cursor: "not-allowed",
94
+ },
95
+ },
96
+ });
97
+
98
+ export const label__subtitle = style({
99
+ display: "block",
100
+ fontWeight: "var(--weight-regular)",
101
+ fontSize: "0.8125rem",
102
+ color: "var(--foreground)",
103
+ });
104
+
105
+ export const label__description = style({
106
+ display: "block",
107
+ margin: 0,
108
+ fontWeight: "var(--weight-regular)",
109
+ fontSize: "0.8125rem",
110
+ lineHeight: 1.4,
111
+ color: "var(--foreground-muted)",
112
+ });
113
+
114
+ export const label__caption = style({
115
+ margin: 0,
116
+ fontWeight: "var(--weight-regular)",
117
+ fontSize: "var(--text-xs)",
118
+ lineHeight: 1.3,
119
+ color: "var(--foreground-subtle, var(--foreground-muted))",
120
+ opacity: 0.75,
121
+ selectors: {
122
+ "&))": {
123
+ display: "block",
124
+ },
125
+ },
126
+ });
127
+
128
+ export const label__title = style({
129
+ fontWeight: "var(--weight-semibold)",
130
+ fontSize: "var(--text-sm)",
131
+ color: "var(--foreground)",
132
+ });
133
+
134
+ /** 동적 키로 클래스 참조용 — `byKey[\`badge--${variant}\`]` 같은 패턴 지원. */
135
+ export const byKey: Record<string, string> = {
136
+ "label": label,
137
+ "label__subtitle": label__subtitle,
138
+ "label__description": label__description,
139
+ "label__caption": label__caption,
140
+ "label__title": label__title,
141
+ };