@renge-ui/react 1.0.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/dist/index.mjs ADDED
@@ -0,0 +1,1349 @@
1
+ // src/provider.tsx
2
+ import { useMemo, useInsertionEffect } from "react";
3
+ import { createRengeTheme } from "@renge-ui/tokens";
4
+
5
+ // src/context.ts
6
+ import { createContext } from "react";
7
+ var RengeContext = createContext(null);
8
+
9
+ // src/hooks/useRenge.ts
10
+ import { useContext } from "react";
11
+ function useRenge() {
12
+ const context = useContext(RengeContext);
13
+ if (!context) {
14
+ throw new Error("useRenge must be used within a RengeProvider");
15
+ }
16
+ return context;
17
+ }
18
+
19
+ // src/provider.tsx
20
+ import { jsx } from "react/jsx-runtime";
21
+ function RengeProvider({
22
+ children,
23
+ config: {
24
+ baseUnit,
25
+ typeBase,
26
+ scaleRatio,
27
+ profile,
28
+ variance,
29
+ varianceSeed,
30
+ includeReset,
31
+ selector
32
+ } = {},
33
+ injectCSS = true
34
+ }) {
35
+ const theme = useMemo(
36
+ () => createRengeTheme({ baseUnit, typeBase, scaleRatio, profile, variance, varianceSeed, includeReset, selector }),
37
+ [baseUnit, typeBase, scaleRatio, profile, variance, varianceSeed, includeReset, selector]
38
+ );
39
+ useInsertionEffect(() => {
40
+ if (!injectCSS || typeof document === "undefined") return;
41
+ const styleId = "renge-theme";
42
+ let styleEl = document.getElementById(styleId);
43
+ if (!styleEl) {
44
+ styleEl = document.createElement("style");
45
+ styleEl.id = styleId;
46
+ document.head.appendChild(styleEl);
47
+ }
48
+ styleEl.textContent = theme.css;
49
+ }, [theme.css, injectCSS]);
50
+ const value = useMemo(() => ({
51
+ theme,
52
+ profile: profile ?? "ocean"
53
+ }), [theme, profile]);
54
+ return /* @__PURE__ */ jsx(RengeContext.Provider, { value, children });
55
+ }
56
+ function useRengeTheme(config) {
57
+ return useMemo(() => createRengeTheme(config), [config]);
58
+ }
59
+
60
+ // src/components/Stack.tsx
61
+ import { forwardRef } from "react";
62
+ import { jsx as jsx2 } from "react/jsx-runtime";
63
+ var Stack = forwardRef(
64
+ function Stack2({
65
+ gap = "3",
66
+ direction = "vertical",
67
+ align = "stretch",
68
+ justify = "start",
69
+ as: Component = "div",
70
+ style,
71
+ children,
72
+ ...props
73
+ }, ref) {
74
+ const alignMap = {
75
+ start: "flex-start",
76
+ center: "center",
77
+ end: "flex-end",
78
+ stretch: "stretch"
79
+ };
80
+ const justifyMap = {
81
+ start: "flex-start",
82
+ center: "center",
83
+ end: "flex-end",
84
+ between: "space-between",
85
+ around: "space-around"
86
+ };
87
+ return /* @__PURE__ */ jsx2(
88
+ Component,
89
+ {
90
+ ref,
91
+ style: {
92
+ display: "flex",
93
+ flexDirection: direction === "vertical" ? "column" : "row",
94
+ gap: `var(--renge-space-${gap})`,
95
+ alignItems: alignMap[align],
96
+ justifyContent: justifyMap[justify],
97
+ ...style
98
+ },
99
+ ...props,
100
+ children
101
+ }
102
+ );
103
+ }
104
+ );
105
+
106
+ // src/components/Grid.tsx
107
+ import { forwardRef as forwardRef2 } from "react";
108
+ import { jsx as jsx3 } from "react/jsx-runtime";
109
+ var Grid = forwardRef2(
110
+ function Grid2({
111
+ columns = 1,
112
+ rows,
113
+ gap = "3",
114
+ gapX,
115
+ gapY,
116
+ align = "stretch",
117
+ justify = "stretch",
118
+ style,
119
+ children,
120
+ ...props
121
+ }, ref) {
122
+ const getTemplate = (value) => {
123
+ if (value === void 0) return void 0;
124
+ if (typeof value === "number") return `repeat(${value}, 1fr)`;
125
+ return value;
126
+ };
127
+ return /* @__PURE__ */ jsx3(
128
+ "div",
129
+ {
130
+ ref,
131
+ style: {
132
+ display: "grid",
133
+ gridTemplateColumns: getTemplate(columns),
134
+ gridTemplateRows: getTemplate(rows),
135
+ columnGap: gapX ? `var(--renge-space-${gapX})` : `var(--renge-space-${gap})`,
136
+ rowGap: gapY ? `var(--renge-space-${gapY})` : `var(--renge-space-${gap})`,
137
+ alignItems: align,
138
+ justifyItems: justify,
139
+ ...style
140
+ },
141
+ ...props,
142
+ children
143
+ }
144
+ );
145
+ }
146
+ );
147
+
148
+ // src/components/Text.tsx
149
+ import { forwardRef as forwardRef3 } from "react";
150
+ import { jsx as jsx4 } from "react/jsx-runtime";
151
+ var Text = forwardRef3(
152
+ function Text2({
153
+ size = "base",
154
+ color = "fg",
155
+ weight = "normal",
156
+ align,
157
+ animation,
158
+ as: Component = "span",
159
+ style,
160
+ children,
161
+ ...props
162
+ }, ref) {
163
+ const weightMap = {
164
+ normal: 400,
165
+ medium: 500,
166
+ semibold: 600,
167
+ bold: 700
168
+ };
169
+ return /* @__PURE__ */ jsx4(
170
+ Component,
171
+ {
172
+ ref,
173
+ style: {
174
+ fontSize: `var(--renge-font-size-${size})`,
175
+ lineHeight: `var(--renge-line-height-${size})`,
176
+ color: `var(--renge-color-${color})`,
177
+ fontWeight: weightMap[weight],
178
+ textAlign: align,
179
+ animation: animation ? `var(--renge-animation-${animation})` : void 0,
180
+ ...style
181
+ },
182
+ ...props,
183
+ children
184
+ }
185
+ );
186
+ }
187
+ );
188
+
189
+ // src/components/Heading.tsx
190
+ import { forwardRef as forwardRef4 } from "react";
191
+ import { jsx as jsx5 } from "react/jsx-runtime";
192
+ var defaultSizeForLevel = {
193
+ 1: "3xl",
194
+ 2: "2xl",
195
+ 3: "xl",
196
+ 4: "lg",
197
+ 5: "lg",
198
+ 6: "lg"
199
+ };
200
+ var Heading = forwardRef4(
201
+ function Heading2({
202
+ level = 2,
203
+ size,
204
+ color = "fg",
205
+ animation,
206
+ style,
207
+ children,
208
+ ...props
209
+ }, ref) {
210
+ const Component = `h${level}`;
211
+ const resolvedSize = size ?? defaultSizeForLevel[level];
212
+ return /* @__PURE__ */ jsx5(
213
+ Component,
214
+ {
215
+ ref,
216
+ style: {
217
+ fontSize: `var(--renge-font-size-${resolvedSize})`,
218
+ lineHeight: `var(--renge-line-height-${resolvedSize})`,
219
+ color: `var(--renge-color-${color})`,
220
+ fontWeight: 600,
221
+ margin: 0,
222
+ animation: animation ? `var(--renge-animation-${animation})` : void 0,
223
+ ...style
224
+ },
225
+ ...props,
226
+ children
227
+ }
228
+ );
229
+ }
230
+ );
231
+
232
+ // src/components/Card.tsx
233
+ import { forwardRef as forwardRef5 } from "react";
234
+ import { jsx as jsx6 } from "react/jsx-runtime";
235
+ var Card = forwardRef5(
236
+ function Card2({
237
+ padding = "4",
238
+ radius = "3",
239
+ variant = "elevated",
240
+ animation,
241
+ style,
242
+ children,
243
+ ...props
244
+ }, ref) {
245
+ const variantStyles = {
246
+ elevated: {
247
+ backgroundColor: `var(--renge-color-bg)`,
248
+ boxShadow: "0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06)"
249
+ },
250
+ outlined: {
251
+ backgroundColor: `var(--renge-color-bg)`,
252
+ border: `1px solid var(--renge-color-border)`
253
+ },
254
+ filled: {
255
+ backgroundColor: `var(--renge-color-bg-subtle)`
256
+ }
257
+ };
258
+ return /* @__PURE__ */ jsx6(
259
+ "div",
260
+ {
261
+ ref,
262
+ style: {
263
+ padding: `var(--renge-space-${padding})`,
264
+ borderRadius: `var(--renge-radius-${radius})`,
265
+ ...variantStyles[variant],
266
+ animation: animation ? `var(--renge-animation-${animation})` : void 0,
267
+ ...style
268
+ },
269
+ ...props,
270
+ children
271
+ }
272
+ );
273
+ }
274
+ );
275
+
276
+ // src/components/Button.tsx
277
+ import { forwardRef as forwardRef6 } from "react";
278
+ import { jsx as jsx7 } from "react/jsx-runtime";
279
+ var sizeStyles = {
280
+ sm: {
281
+ padding: "var(--renge-space-1) var(--renge-space-2)",
282
+ fontSize: "var(--renge-font-size-sm)"
283
+ },
284
+ md: {
285
+ padding: "var(--renge-space-2) var(--renge-space-4)",
286
+ fontSize: "var(--renge-font-size-base)"
287
+ },
288
+ lg: {
289
+ padding: "var(--renge-space-3) var(--renge-space-5)",
290
+ fontSize: "var(--renge-font-size-lg)"
291
+ }
292
+ };
293
+ var Button = forwardRef6(
294
+ function Button2({
295
+ size = "md",
296
+ variant = "solid",
297
+ colorScheme = "accent",
298
+ fullWidth = false,
299
+ animation,
300
+ style,
301
+ children,
302
+ ...props
303
+ }, ref) {
304
+ const getVariantStyles = () => {
305
+ switch (variant) {
306
+ case "solid":
307
+ return {
308
+ backgroundColor: `var(--renge-color-${colorScheme})`,
309
+ color: "var(--renge-color-fg-inverse)",
310
+ border: "none"
311
+ };
312
+ case "outline":
313
+ return {
314
+ backgroundColor: "transparent",
315
+ color: `var(--renge-color-${colorScheme})`,
316
+ border: `1px solid var(--renge-color-${colorScheme})`
317
+ };
318
+ case "ghost":
319
+ return {
320
+ backgroundColor: "transparent",
321
+ color: `var(--renge-color-${colorScheme})`,
322
+ border: "none"
323
+ };
324
+ }
325
+ };
326
+ return /* @__PURE__ */ jsx7(
327
+ "button",
328
+ {
329
+ ref,
330
+ style: {
331
+ ...sizeStyles[size],
332
+ ...getVariantStyles(),
333
+ borderRadius: "var(--renge-radius-2)",
334
+ fontWeight: 500,
335
+ cursor: "pointer",
336
+ transition: `all var(--renge-duration-2) var(--renge-easing-ease-out)`,
337
+ width: fullWidth ? "100%" : void 0,
338
+ animation: animation ? `var(--renge-animation-${animation})` : void 0,
339
+ ...style
340
+ },
341
+ ...props,
342
+ children
343
+ }
344
+ );
345
+ }
346
+ );
347
+
348
+ // src/components/Divider.tsx
349
+ import { forwardRef as forwardRef7 } from "react";
350
+ import { jsx as jsx8 } from "react/jsx-runtime";
351
+ var Divider = forwardRef7(
352
+ function Divider2({
353
+ orientation = "horizontal",
354
+ spacing = "3",
355
+ color = "border-subtle",
356
+ style,
357
+ ...props
358
+ }, ref) {
359
+ const isHorizontal = orientation === "horizontal";
360
+ return /* @__PURE__ */ jsx8(
361
+ "hr",
362
+ {
363
+ ref,
364
+ style: {
365
+ border: "none",
366
+ backgroundColor: `var(--renge-color-${color})`,
367
+ ...isHorizontal ? {
368
+ height: "1px",
369
+ width: "100%",
370
+ marginBlock: `var(--renge-space-${spacing})`
371
+ } : {
372
+ width: "1px",
373
+ height: "100%",
374
+ marginInline: `var(--renge-space-${spacing})`
375
+ },
376
+ ...style
377
+ },
378
+ ...props
379
+ }
380
+ );
381
+ }
382
+ );
383
+
384
+ // src/components/Section.tsx
385
+ import { forwardRef as forwardRef8 } from "react";
386
+ import { jsx as jsx9 } from "react/jsx-runtime";
387
+ var maxWidthMap = {
388
+ sm: "640px",
389
+ md: "768px",
390
+ lg: "1024px",
391
+ xl: "1280px",
392
+ full: "100%",
393
+ none: void 0
394
+ };
395
+ var Section = forwardRef8(
396
+ function Section2({
397
+ padding,
398
+ paddingX = "4",
399
+ paddingY = "6",
400
+ maxWidth = "lg",
401
+ center = true,
402
+ animation,
403
+ as: Component = "section",
404
+ style,
405
+ children,
406
+ ...props
407
+ }, ref) {
408
+ return /* @__PURE__ */ jsx9(
409
+ Component,
410
+ {
411
+ ref,
412
+ style: {
413
+ paddingInline: padding ? `var(--renge-space-${padding})` : `var(--renge-space-${paddingX})`,
414
+ paddingBlock: padding ? `var(--renge-space-${padding})` : `var(--renge-space-${paddingY})`,
415
+ maxWidth: maxWidthMap[maxWidth],
416
+ marginInline: center ? "auto" : void 0,
417
+ animation: animation ? `var(--renge-animation-${animation})` : void 0,
418
+ ...style
419
+ },
420
+ ...props,
421
+ children
422
+ }
423
+ );
424
+ }
425
+ );
426
+
427
+ // src/components/Badge.tsx
428
+ import { forwardRef as forwardRef9 } from "react";
429
+ import { jsx as jsx10 } from "react/jsx-runtime";
430
+ var colorVars = (variant) => {
431
+ if (variant === "neutral") {
432
+ return {
433
+ background: "var(--renge-color-bg-subtle)",
434
+ color: "var(--renge-color-fg-muted)",
435
+ borderColor: "var(--renge-color-border-subtle)"
436
+ };
437
+ }
438
+ return {
439
+ background: `var(--renge-color-${variant}-subtle)`,
440
+ color: `var(--renge-color-${variant})`,
441
+ borderColor: `var(--renge-color-${variant})`
442
+ };
443
+ };
444
+ var sizeStyles2 = {
445
+ sm: { padding: "var(--renge-space-1) var(--renge-space-2)", fontSize: "var(--renge-font-size-xs)" },
446
+ md: { padding: "var(--renge-space-1) var(--renge-space-3)", fontSize: "var(--renge-font-size-xs)" },
447
+ lg: { padding: "var(--renge-space-2) var(--renge-space-3)", fontSize: "var(--renge-font-size-sm)" }
448
+ };
449
+ var Badge = forwardRef9(
450
+ function Badge2({ variant = "neutral", size = "md", style, children, ...props }, ref) {
451
+ const { background, color, borderColor } = colorVars(variant);
452
+ return /* @__PURE__ */ jsx10(
453
+ "span",
454
+ {
455
+ ref,
456
+ style: {
457
+ display: "inline-flex",
458
+ alignItems: "center",
459
+ lineHeight: 1,
460
+ fontWeight: 500,
461
+ borderRadius: "var(--renge-radius-full)",
462
+ border: `1px solid ${borderColor}`,
463
+ background,
464
+ color,
465
+ ...sizeStyles2[size],
466
+ ...style
467
+ },
468
+ ...props,
469
+ children
470
+ }
471
+ );
472
+ }
473
+ );
474
+
475
+ // src/components/Avatar.tsx
476
+ import { forwardRef as forwardRef10 } from "react";
477
+ import { jsx as jsx11 } from "react/jsx-runtime";
478
+ var sizePx = {
479
+ "1": 20,
480
+ "2": 32,
481
+ "3": 52,
482
+ "4": 84,
483
+ "5": 136
484
+ };
485
+ var fontSizeFor = {
486
+ "1": "var(--renge-font-size-xs)",
487
+ "2": "var(--renge-font-size-sm)",
488
+ "3": "var(--renge-font-size-base)",
489
+ "4": "var(--renge-font-size-lg)",
490
+ "5": "var(--renge-font-size-xl)"
491
+ };
492
+ var Avatar = forwardRef10(
493
+ function Avatar2({ src, alt, initials, size = "3", shape = "circle", style, ...props }, ref) {
494
+ const px = sizePx[size];
495
+ const radius = shape === "circle" ? "var(--renge-radius-full)" : "var(--renge-radius-3)";
496
+ const label = initials ? initials.slice(0, 2).toUpperCase() : void 0;
497
+ return /* @__PURE__ */ jsx11(
498
+ "div",
499
+ {
500
+ ref,
501
+ "aria-label": alt,
502
+ role: alt ? "img" : void 0,
503
+ style: {
504
+ display: "inline-flex",
505
+ alignItems: "center",
506
+ justifyContent: "center",
507
+ width: px,
508
+ height: px,
509
+ borderRadius: radius,
510
+ overflow: "hidden",
511
+ flexShrink: 0,
512
+ background: src ? void 0 : "var(--renge-color-bg-muted)",
513
+ color: "var(--renge-color-fg-inverse)",
514
+ fontSize: fontSizeFor[size],
515
+ fontWeight: 600,
516
+ userSelect: "none",
517
+ ...style
518
+ },
519
+ ...props,
520
+ children: src ? /* @__PURE__ */ jsx11(
521
+ "img",
522
+ {
523
+ src,
524
+ alt: alt ?? "",
525
+ style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
526
+ }
527
+ ) : label
528
+ }
529
+ );
530
+ }
531
+ );
532
+
533
+ // src/components/Spinner.tsx
534
+ import { forwardRef as forwardRef11 } from "react";
535
+ import { jsx as jsx12 } from "react/jsx-runtime";
536
+ if (typeof document !== "undefined") {
537
+ const id = "renge-spinner-kf";
538
+ if (!document.getElementById(id)) {
539
+ const s = document.createElement("style");
540
+ s.id = id;
541
+ s.textContent = "@keyframes rengeSpinnerSpin { to { transform: rotate(360deg); } }";
542
+ document.head.appendChild(s);
543
+ }
544
+ }
545
+ var sizePx2 = { sm: 16, md: 24, lg: 32 };
546
+ var borderWidth = { sm: "2px", md: "2px", lg: "3px" };
547
+ var Spinner = forwardRef11(
548
+ function Spinner2({ size = "md", color = "accent", label = "Loading", style, ...props }, ref) {
549
+ const px = sizePx2[size];
550
+ const bw = borderWidth[size];
551
+ return /* @__PURE__ */ jsx12(
552
+ "span",
553
+ {
554
+ ref,
555
+ role: "status",
556
+ "aria-label": label,
557
+ style: {
558
+ display: "inline-block",
559
+ width: px,
560
+ height: px,
561
+ borderRadius: "var(--renge-radius-full)",
562
+ border: `${bw} solid var(--renge-color-border-subtle)`,
563
+ borderTopColor: `var(--renge-color-${color})`,
564
+ animation: `rengeSpinnerSpin var(--renge-duration-3) linear infinite`,
565
+ flexShrink: 0,
566
+ ...style
567
+ },
568
+ ...props
569
+ }
570
+ );
571
+ }
572
+ );
573
+
574
+ // src/components/Progress.tsx
575
+ import { forwardRef as forwardRef12 } from "react";
576
+ import { jsx as jsx13 } from "react/jsx-runtime";
577
+ var trackHeight = { sm: 4, md: 8, lg: 12 };
578
+ var Progress = forwardRef12(
579
+ function Progress2({ value, color = "accent", size = "md", radius = "full", label, style, ...props }, ref) {
580
+ const clamped = Math.min(100, Math.max(0, value));
581
+ const borderRadius = radius === "full" ? "var(--renge-radius-full)" : "0px";
582
+ const height = trackHeight[size];
583
+ return /* @__PURE__ */ jsx13(
584
+ "div",
585
+ {
586
+ ref,
587
+ role: "progressbar",
588
+ "aria-valuenow": clamped,
589
+ "aria-valuemin": 0,
590
+ "aria-valuemax": 100,
591
+ "aria-label": label,
592
+ style: {
593
+ width: "100%",
594
+ height,
595
+ borderRadius,
596
+ background: "var(--renge-color-bg-muted)",
597
+ overflow: "hidden",
598
+ ...style
599
+ },
600
+ ...props,
601
+ children: /* @__PURE__ */ jsx13(
602
+ "div",
603
+ {
604
+ style: {
605
+ height: "100%",
606
+ width: `${clamped}%`,
607
+ borderRadius,
608
+ background: `var(--renge-color-${color})`,
609
+ transition: `width var(--renge-duration-3) var(--renge-easing-ease-out)`
610
+ }
611
+ }
612
+ )
613
+ }
614
+ );
615
+ }
616
+ );
617
+
618
+ // src/components/Input.tsx
619
+ import { forwardRef as forwardRef13 } from "react";
620
+ import { jsx as jsx14 } from "react/jsx-runtime";
621
+ var sizeStyles3 = {
622
+ sm: {
623
+ padding: "var(--renge-space-1) var(--renge-space-2)",
624
+ fontSize: "var(--renge-font-size-sm)"
625
+ },
626
+ md: {
627
+ padding: "var(--renge-space-2) var(--renge-space-3)",
628
+ fontSize: "var(--renge-font-size-base)"
629
+ },
630
+ lg: {
631
+ padding: "var(--renge-space-2) var(--renge-space-4)",
632
+ fontSize: "var(--renge-font-size-base)"
633
+ }
634
+ };
635
+ var stateColor = {
636
+ default: "var(--renge-color-border)",
637
+ error: "var(--renge-color-danger)",
638
+ success: "var(--renge-color-success)"
639
+ };
640
+ var Input = forwardRef13(
641
+ function Input2({ size = "md", state = "default", fullWidth = false, style, ...props }, ref) {
642
+ return /* @__PURE__ */ jsx14(
643
+ "input",
644
+ {
645
+ ref,
646
+ style: {
647
+ ...sizeStyles3[size],
648
+ display: "block",
649
+ width: fullWidth ? "100%" : void 0,
650
+ background: "var(--renge-color-bg)",
651
+ color: "var(--renge-color-fg)",
652
+ border: `1px solid ${stateColor[state]}`,
653
+ borderRadius: "var(--renge-radius-2)",
654
+ outline: "none",
655
+ transition: `border-color var(--renge-duration-1) var(--renge-easing-ease-out)`,
656
+ // Focus is handled via :focus-visible pseudo-class but inline styles can't do that.
657
+ // We set a CSS custom property approach via box-shadow as focus ring instead.
658
+ boxSizing: "border-box",
659
+ fontFamily: "inherit",
660
+ ...style
661
+ },
662
+ onFocus: (e) => {
663
+ e.currentTarget.style.outline = `2px solid var(--renge-color-border-focus)`;
664
+ e.currentTarget.style.outlineOffset = "2px";
665
+ props.onFocus?.(e);
666
+ },
667
+ onBlur: (e) => {
668
+ e.currentTarget.style.outline = "none";
669
+ props.onBlur?.(e);
670
+ },
671
+ ...props
672
+ }
673
+ );
674
+ }
675
+ );
676
+
677
+ // src/components/Chip.tsx
678
+ import { forwardRef as forwardRef14 } from "react";
679
+ import { jsx as jsx15, jsxs } from "react/jsx-runtime";
680
+ var colorVars2 = (variant) => {
681
+ if (variant === "neutral") {
682
+ return {
683
+ background: "var(--renge-color-bg-subtle)",
684
+ color: "var(--renge-color-fg-muted)",
685
+ borderColor: "var(--renge-color-border-subtle)"
686
+ };
687
+ }
688
+ return {
689
+ background: `var(--renge-color-${variant}-subtle)`,
690
+ color: `var(--renge-color-${variant})`,
691
+ borderColor: `var(--renge-color-${variant})`
692
+ };
693
+ };
694
+ var Chip = forwardRef14(
695
+ function Chip2({ variant = "neutral", onDismiss, style, children, ...props }, ref) {
696
+ const { background, color, borderColor } = colorVars2(variant);
697
+ return /* @__PURE__ */ jsxs(
698
+ "span",
699
+ {
700
+ ref,
701
+ style: {
702
+ display: "inline-flex",
703
+ alignItems: "center",
704
+ gap: "var(--renge-space-2)",
705
+ padding: "var(--renge-space-2) var(--renge-space-3)",
706
+ fontSize: "var(--renge-font-size-sm)",
707
+ fontWeight: 500,
708
+ lineHeight: 1,
709
+ borderRadius: "var(--renge-radius-full)",
710
+ border: `1px solid ${borderColor}`,
711
+ background,
712
+ color,
713
+ ...style
714
+ },
715
+ ...props,
716
+ children: [
717
+ children,
718
+ onDismiss && /* @__PURE__ */ jsx15(
719
+ "button",
720
+ {
721
+ onClick: onDismiss,
722
+ "aria-label": "Dismiss",
723
+ style: {
724
+ display: "inline-flex",
725
+ alignItems: "center",
726
+ justifyContent: "center",
727
+ background: "none",
728
+ border: "none",
729
+ padding: 0,
730
+ cursor: "pointer",
731
+ color: "inherit",
732
+ fontSize: "var(--renge-font-size-sm)",
733
+ lineHeight: 1,
734
+ opacity: 0.7
735
+ },
736
+ children: "\xD7"
737
+ }
738
+ )
739
+ ]
740
+ }
741
+ );
742
+ }
743
+ );
744
+
745
+ // src/components/Alert.tsx
746
+ import { forwardRef as forwardRef15 } from "react";
747
+ import { jsx as jsx16, jsxs as jsxs2 } from "react/jsx-runtime";
748
+ var Alert = forwardRef15(
749
+ function Alert2({ status = "info", title, style, children, ...props }, ref) {
750
+ return /* @__PURE__ */ jsxs2(
751
+ "div",
752
+ {
753
+ ref,
754
+ role: "alert",
755
+ style: {
756
+ display: "flex",
757
+ flexDirection: "column",
758
+ gap: title && children ? "var(--renge-space-1)" : void 0,
759
+ padding: "var(--renge-space-3) var(--renge-space-4)",
760
+ borderRadius: "var(--renge-radius-2)",
761
+ borderLeft: `3px solid var(--renge-color-${status})`,
762
+ background: `var(--renge-color-${status}-subtle)`,
763
+ color: "var(--renge-color-fg)",
764
+ ...style
765
+ },
766
+ ...props,
767
+ children: [
768
+ title && /* @__PURE__ */ jsx16(
769
+ "strong",
770
+ {
771
+ style: {
772
+ fontSize: "var(--renge-font-size-sm)",
773
+ fontWeight: 600,
774
+ color: "var(--renge-color-fg)"
775
+ },
776
+ children: title
777
+ }
778
+ ),
779
+ children && /* @__PURE__ */ jsx16("span", { style: { fontSize: "var(--renge-font-size-sm)", color: "var(--renge-color-fg)" }, children })
780
+ ]
781
+ }
782
+ );
783
+ }
784
+ );
785
+
786
+ // src/components/FormField.tsx
787
+ import { forwardRef as forwardRef16 } from "react";
788
+ import { jsx as jsx17, jsxs as jsxs3 } from "react/jsx-runtime";
789
+ var FormField = forwardRef16(
790
+ function FormField2({ label, htmlFor, helperText, errorText, required, style, children, ...props }, ref) {
791
+ const subText = errorText ?? helperText;
792
+ const subColor = errorText ? "var(--renge-color-danger)" : "var(--renge-color-fg-muted)";
793
+ return /* @__PURE__ */ jsxs3(
794
+ "div",
795
+ {
796
+ ref,
797
+ style: {
798
+ display: "flex",
799
+ flexDirection: "column",
800
+ gap: "var(--renge-space-2)",
801
+ ...style
802
+ },
803
+ ...props,
804
+ children: [
805
+ /* @__PURE__ */ jsxs3(
806
+ "label",
807
+ {
808
+ htmlFor,
809
+ style: {
810
+ fontSize: "var(--renge-font-size-sm)",
811
+ fontWeight: 500,
812
+ color: "var(--renge-color-fg)",
813
+ display: "flex",
814
+ alignItems: "center",
815
+ gap: "var(--renge-space-1)"
816
+ },
817
+ children: [
818
+ label,
819
+ required && /* @__PURE__ */ jsx17("span", { "aria-hidden": "true", style: { color: "var(--renge-color-danger)" }, children: "*" })
820
+ ]
821
+ }
822
+ ),
823
+ children,
824
+ subText && /* @__PURE__ */ jsx17(
825
+ "span",
826
+ {
827
+ style: {
828
+ fontSize: "var(--renge-font-size-xs)",
829
+ color: subColor
830
+ },
831
+ children: subText
832
+ }
833
+ )
834
+ ]
835
+ }
836
+ );
837
+ }
838
+ );
839
+
840
+ // src/components/Stat.tsx
841
+ import { forwardRef as forwardRef17 } from "react";
842
+ import { jsx as jsx18, jsxs as jsxs4 } from "react/jsx-runtime";
843
+ var trendColor = {
844
+ up: "var(--renge-color-success)",
845
+ down: "var(--renge-color-danger)",
846
+ neutral: "var(--renge-color-fg-muted)"
847
+ };
848
+ var trendBg = {
849
+ up: "var(--renge-color-success-subtle)",
850
+ down: "var(--renge-color-danger-subtle)",
851
+ neutral: "var(--renge-color-bg-subtle)"
852
+ };
853
+ var trendBorder = {
854
+ up: "var(--renge-color-success)",
855
+ down: "var(--renge-color-danger)",
856
+ neutral: "var(--renge-color-border-subtle)"
857
+ };
858
+ var trendSymbol = {
859
+ up: "\u2191",
860
+ down: "\u2193",
861
+ neutral: "\u2014"
862
+ };
863
+ var Stat = forwardRef17(
864
+ function Stat2({ value, label, trend, trendValue, caption, style, ...props }, ref) {
865
+ return /* @__PURE__ */ jsxs4(
866
+ "div",
867
+ {
868
+ ref,
869
+ style: {
870
+ display: "flex",
871
+ flexDirection: "column",
872
+ padding: "var(--renge-space-4)",
873
+ ...style
874
+ },
875
+ ...props,
876
+ children: [
877
+ /* @__PURE__ */ jsx18(
878
+ "span",
879
+ {
880
+ style: {
881
+ fontSize: "var(--renge-font-size-xs)",
882
+ color: "var(--renge-color-fg-muted)",
883
+ marginBottom: "var(--renge-space-1)",
884
+ fontWeight: 500
885
+ },
886
+ children: label
887
+ }
888
+ ),
889
+ /* @__PURE__ */ jsxs4(
890
+ "div",
891
+ {
892
+ style: {
893
+ display: "flex",
894
+ alignItems: "baseline",
895
+ gap: "var(--renge-space-3)",
896
+ flexWrap: "wrap"
897
+ },
898
+ children: [
899
+ /* @__PURE__ */ jsx18(
900
+ "span",
901
+ {
902
+ style: {
903
+ fontSize: "var(--renge-font-size-3xl)",
904
+ lineHeight: 1.2,
905
+ fontWeight: 600,
906
+ color: "var(--renge-color-fg)"
907
+ },
908
+ children: value
909
+ }
910
+ ),
911
+ trend && trendValue && /* @__PURE__ */ jsxs4(
912
+ "span",
913
+ {
914
+ style: {
915
+ display: "inline-flex",
916
+ alignItems: "center",
917
+ gap: "var(--renge-space-1)",
918
+ padding: "var(--renge-space-1) var(--renge-space-2)",
919
+ fontSize: "var(--renge-font-size-xs)",
920
+ fontWeight: 500,
921
+ lineHeight: 1,
922
+ borderRadius: "var(--renge-radius-full)",
923
+ border: `1px solid ${trendBorder[trend]}`,
924
+ background: trendBg[trend],
925
+ color: trendColor[trend]
926
+ },
927
+ children: [
928
+ trendSymbol[trend],
929
+ " ",
930
+ trendValue
931
+ ]
932
+ }
933
+ )
934
+ ]
935
+ }
936
+ ),
937
+ caption && /* @__PURE__ */ jsx18(
938
+ "span",
939
+ {
940
+ style: {
941
+ fontSize: "var(--renge-font-size-xs)",
942
+ color: "var(--renge-color-fg-muted)",
943
+ marginTop: "var(--renge-space-1)"
944
+ },
945
+ children: caption
946
+ }
947
+ )
948
+ ]
949
+ }
950
+ );
951
+ }
952
+ );
953
+
954
+ // src/components/Navbar.tsx
955
+ import { forwardRef as forwardRef18 } from "react";
956
+ import { jsx as jsx19 } from "react/jsx-runtime";
957
+ var Navbar = forwardRef18(
958
+ function Navbar2({ sticky = false, border = true, height = "56px", paddingX = "5", style, children, ...props }, ref) {
959
+ return /* @__PURE__ */ jsx19(
960
+ "nav",
961
+ {
962
+ ref,
963
+ style: {
964
+ display: "flex",
965
+ alignItems: "center",
966
+ minHeight: height,
967
+ padding: `0 var(--renge-space-${paddingX})`,
968
+ background: "var(--renge-color-bg)",
969
+ borderBottom: border ? "1px solid var(--renge-color-border-subtle)" : void 0,
970
+ position: sticky ? "sticky" : void 0,
971
+ top: sticky ? 0 : void 0,
972
+ zIndex: sticky ? 100 : void 0,
973
+ boxSizing: "border-box",
974
+ ...style
975
+ },
976
+ ...props,
977
+ children
978
+ }
979
+ );
980
+ }
981
+ );
982
+
983
+ // src/components/EnergyRing.tsx
984
+ import { forwardRef as forwardRef19 } from "react";
985
+ import { jsx as jsx20, jsxs as jsxs5 } from "react/jsx-runtime";
986
+ var KEYFRAMES = `
987
+ @keyframes rengeRingPulse {
988
+ 0%, 100% { opacity: 1; }
989
+ 50% { opacity: 0.382; }
990
+ }
991
+ `;
992
+ if (typeof document !== "undefined") {
993
+ const id = "__renge-ring-keyframes__";
994
+ if (!document.getElementById(id)) {
995
+ const style = document.createElement("style");
996
+ style.id = id;
997
+ style.textContent = KEYFRAMES;
998
+ document.head.appendChild(style);
999
+ }
1000
+ }
1001
+ var SIZE_CONFIG = {
1002
+ sm: { diameter: 32, stroke: 3, fontSize: "var(--renge-font-size-xs)" },
1003
+ md: { diameter: 52, stroke: 4, fontSize: "var(--renge-font-size-xs)" },
1004
+ lg: { diameter: 84, stroke: 5, fontSize: "var(--renge-font-size-sm)" },
1005
+ xl: { diameter: 136, stroke: 6, fontSize: "var(--renge-font-size-base)" }
1006
+ };
1007
+ var PULSE_DURATION = {
1008
+ rest: "var(--renge-duration-7)",
1009
+ // 2100ms — slow, resting breath
1010
+ active: "var(--renge-duration-5)",
1011
+ // 800ms — normal active pulse
1012
+ fire: "var(--renge-duration-4)"
1013
+ // 500ms — intense, rapid
1014
+ };
1015
+ var EnergyRing = forwardRef19(
1016
+ function EnergyRing2({
1017
+ value,
1018
+ size = "md",
1019
+ color = "accent",
1020
+ pulse = false,
1021
+ rate = "active",
1022
+ label,
1023
+ style,
1024
+ ...props
1025
+ }, ref) {
1026
+ const { diameter, stroke, fontSize } = SIZE_CONFIG[size];
1027
+ const radius = (diameter - stroke) / 2;
1028
+ const circumference = 2 * Math.PI * radius;
1029
+ const clampedValue = Math.min(100, Math.max(0, value));
1030
+ const offset = circumference * (1 - clampedValue / 100);
1031
+ const center = diameter / 2;
1032
+ const colorVar = `var(--renge-color-${color})`;
1033
+ const colorSubtleVar = `var(--renge-color-${color}-subtle)`;
1034
+ const pulseStyle = pulse ? {
1035
+ animation: `rengeRingPulse ${PULSE_DURATION[rate]} var(--renge-easing-ease-in-out) infinite`
1036
+ } : {};
1037
+ const showLabel = label !== null && (size === "lg" || size === "xl");
1038
+ const labelText = label !== void 0 ? label : `${clampedValue}%`;
1039
+ return /* @__PURE__ */ jsxs5(
1040
+ "div",
1041
+ {
1042
+ ref,
1043
+ role: "progressbar",
1044
+ "aria-valuenow": clampedValue,
1045
+ "aria-valuemin": 0,
1046
+ "aria-valuemax": 100,
1047
+ style: {
1048
+ display: "inline-flex",
1049
+ alignItems: "center",
1050
+ justifyContent: "center",
1051
+ width: `${diameter}px`,
1052
+ height: `${diameter}px`,
1053
+ position: "relative",
1054
+ flexShrink: 0,
1055
+ ...style
1056
+ },
1057
+ ...props,
1058
+ children: [
1059
+ /* @__PURE__ */ jsxs5(
1060
+ "svg",
1061
+ {
1062
+ width: diameter,
1063
+ height: diameter,
1064
+ viewBox: `0 0 ${diameter} ${diameter}`,
1065
+ style: { position: "absolute", top: 0, left: 0, ...pulseStyle },
1066
+ "aria-hidden": "true",
1067
+ children: [
1068
+ /* @__PURE__ */ jsx20(
1069
+ "circle",
1070
+ {
1071
+ cx: center,
1072
+ cy: center,
1073
+ r: radius,
1074
+ fill: "none",
1075
+ stroke: colorSubtleVar,
1076
+ strokeWidth: stroke
1077
+ }
1078
+ ),
1079
+ /* @__PURE__ */ jsx20(
1080
+ "circle",
1081
+ {
1082
+ cx: center,
1083
+ cy: center,
1084
+ r: radius,
1085
+ fill: "none",
1086
+ stroke: colorVar,
1087
+ strokeWidth: stroke,
1088
+ strokeLinecap: "round",
1089
+ strokeDasharray: circumference,
1090
+ strokeDashoffset: offset,
1091
+ style: {
1092
+ transform: "rotate(-90deg)",
1093
+ transformOrigin: `${center}px ${center}px`,
1094
+ transition: `stroke-dashoffset var(--renge-duration-5) var(--renge-easing-ease-out)`
1095
+ }
1096
+ }
1097
+ )
1098
+ ]
1099
+ }
1100
+ ),
1101
+ showLabel && /* @__PURE__ */ jsx20(
1102
+ "span",
1103
+ {
1104
+ style: {
1105
+ fontSize,
1106
+ color: colorVar,
1107
+ fontVariantNumeric: "tabular-nums",
1108
+ lineHeight: 1,
1109
+ pointerEvents: "none"
1110
+ },
1111
+ children: labelText
1112
+ }
1113
+ )
1114
+ ]
1115
+ }
1116
+ );
1117
+ }
1118
+ );
1119
+
1120
+ // src/components/Pulse.tsx
1121
+ import { forwardRef as forwardRef20 } from "react";
1122
+ import { jsx as jsx21, jsxs as jsxs6 } from "react/jsx-runtime";
1123
+ var KEYFRAMES2 = `
1124
+ @keyframes rengePulseBreathe {
1125
+ 0%, 100% {
1126
+ transform: scale(1);
1127
+ opacity: 1;
1128
+ }
1129
+ 50% {
1130
+ transform: scale(1.618);
1131
+ opacity: 0.382;
1132
+ }
1133
+ }
1134
+ @keyframes rengePulseRipple {
1135
+ 0% { transform: scale(1); opacity: 0.5; }
1136
+ 100% { transform: scale(2.618); opacity: 0; }
1137
+ }
1138
+ `;
1139
+ if (typeof document !== "undefined") {
1140
+ const id = "__renge-pulse-keyframes__";
1141
+ if (!document.getElementById(id)) {
1142
+ const style = document.createElement("style");
1143
+ style.id = id;
1144
+ style.textContent = KEYFRAMES2;
1145
+ document.head.appendChild(style);
1146
+ }
1147
+ }
1148
+ var SIZE_PX = {
1149
+ sm: 6,
1150
+ md: 10,
1151
+ lg: 16
1152
+ };
1153
+ var RATE_DURATION = {
1154
+ rest: "var(--renge-duration-7)",
1155
+ // 2100ms
1156
+ active: "var(--renge-duration-5)",
1157
+ // 800ms
1158
+ fire: "var(--renge-duration-4)"
1159
+ // 500ms
1160
+ };
1161
+ var Pulse = forwardRef20(
1162
+ function Pulse2({
1163
+ rate = "active",
1164
+ color = "accent",
1165
+ size = "md",
1166
+ ripple = false,
1167
+ style,
1168
+ ...props
1169
+ }, ref) {
1170
+ const diameter = SIZE_PX[size];
1171
+ const duration = RATE_DURATION[rate];
1172
+ const colorVar = `var(--renge-color-${color})`;
1173
+ return /* @__PURE__ */ jsxs6(
1174
+ "span",
1175
+ {
1176
+ ref,
1177
+ "aria-hidden": "true",
1178
+ style: {
1179
+ display: "inline-flex",
1180
+ alignItems: "center",
1181
+ justifyContent: "center",
1182
+ position: "relative",
1183
+ width: `${diameter}px`,
1184
+ height: `${diameter}px`,
1185
+ flexShrink: 0,
1186
+ ...style
1187
+ },
1188
+ ...props,
1189
+ children: [
1190
+ ripple && /* @__PURE__ */ jsx21(
1191
+ "span",
1192
+ {
1193
+ style: {
1194
+ position: "absolute",
1195
+ inset: 0,
1196
+ borderRadius: "9999px",
1197
+ backgroundColor: colorVar,
1198
+ animation: `rengePulseRipple ${duration} var(--renge-easing-ease-out) infinite`
1199
+ }
1200
+ }
1201
+ ),
1202
+ /* @__PURE__ */ jsx21(
1203
+ "span",
1204
+ {
1205
+ style: {
1206
+ width: `${diameter}px`,
1207
+ height: `${diameter}px`,
1208
+ borderRadius: "9999px",
1209
+ backgroundColor: colorVar,
1210
+ display: "block",
1211
+ animation: `rengePulseBreathe ${duration} var(--renge-easing-ease-in-out) infinite`
1212
+ }
1213
+ }
1214
+ )
1215
+ ]
1216
+ }
1217
+ );
1218
+ }
1219
+ );
1220
+
1221
+ // src/components/FlowField.tsx
1222
+ import { forwardRef as forwardRef21, useMemo as useMemo2 } from "react";
1223
+ import { phyllotaxis } from "@renge-ui/tokens";
1224
+ import { jsx as jsx22 } from "react/jsx-runtime";
1225
+ var KEYFRAMES3 = `
1226
+ @keyframes rengeFlowPulse {
1227
+ 0%, 100% { opacity: var(--ff-base-opacity); r: var(--ff-base-r); }
1228
+ 50% { opacity: var(--ff-peak-opacity); r: var(--ff-peak-r); }
1229
+ }
1230
+ `;
1231
+ if (typeof document !== "undefined") {
1232
+ const id = "__renge-flow-keyframes__";
1233
+ if (!document.getElementById(id)) {
1234
+ const style = document.createElement("style");
1235
+ style.id = id;
1236
+ style.textContent = KEYFRAMES3;
1237
+ document.head.appendChild(style);
1238
+ }
1239
+ }
1240
+ var ENERGY_CONFIG = {
1241
+ void: { density: 0.2, baseOpacity: 0.06, peakOpacity: 0.15, baseR: 1, peakR: 1.4, duration: "var(--renge-duration-8)" },
1242
+ // 3400ms
1243
+ rest: { density: 0.45, baseOpacity: 0.12, peakOpacity: 0.28, baseR: 1.2, peakR: 1.8, duration: "var(--renge-duration-6)" },
1244
+ // 1300ms
1245
+ active: { density: 0.72, baseOpacity: 0.22, peakOpacity: 0.55, baseR: 1.4, peakR: 2.2, duration: "var(--renge-duration-5)" },
1246
+ // 800ms
1247
+ fire: { density: 1, baseOpacity: 0.35, peakOpacity: 0.82, baseR: 1.6, peakR: 2.618, duration: "var(--renge-duration-4)" }
1248
+ // 500ms
1249
+ };
1250
+ var FlowField = forwardRef21(
1251
+ function FlowField2({
1252
+ count = 144,
1253
+ size = 400,
1254
+ energy = "active",
1255
+ color = "accent",
1256
+ style,
1257
+ ...props
1258
+ }, ref) {
1259
+ const cfg = ENERGY_CONFIG[energy];
1260
+ const visibleCount = Math.round(count * cfg.density);
1261
+ const colorVar = `var(--renge-color-${color})`;
1262
+ const points = useMemo2(() => {
1263
+ const spread = size / (2 * Math.sqrt(count));
1264
+ return phyllotaxis({ count, spread, scale: 1 });
1265
+ }, [count, size]);
1266
+ return /* @__PURE__ */ jsx22(
1267
+ "div",
1268
+ {
1269
+ ref,
1270
+ style: {
1271
+ width: `${size}px`,
1272
+ height: `${size}px`,
1273
+ position: "relative",
1274
+ overflow: "hidden",
1275
+ flexShrink: 0,
1276
+ // CSS vars for SVG animation — set at container level
1277
+ ["--ff-base-opacity"]: String(cfg.baseOpacity),
1278
+ ["--ff-peak-opacity"]: String(cfg.peakOpacity),
1279
+ ["--ff-base-r"]: `${cfg.baseR}px`,
1280
+ ["--ff-peak-r"]: `${cfg.peakR}px`,
1281
+ ...style
1282
+ },
1283
+ ...props,
1284
+ children: /* @__PURE__ */ jsx22(
1285
+ "svg",
1286
+ {
1287
+ width: size,
1288
+ height: size,
1289
+ viewBox: `${-size / 2} ${-size / 2} ${size} ${size}`,
1290
+ "aria-hidden": "true",
1291
+ style: { display: "block" },
1292
+ children: points.slice(0, visibleCount).map((pt) => {
1293
+ const delayFraction = pt.index / visibleCount;
1294
+ const delayMs = delayFraction * 2100;
1295
+ return /* @__PURE__ */ jsx22(
1296
+ "circle",
1297
+ {
1298
+ cx: pt.x,
1299
+ cy: pt.y,
1300
+ r: cfg.baseR,
1301
+ fill: colorVar,
1302
+ style: {
1303
+ animation: `rengeFlowPulse ${cfg.duration} var(--renge-easing-ease-in-out) ${delayMs.toFixed(0)}ms infinite`
1304
+ }
1305
+ },
1306
+ pt.index
1307
+ );
1308
+ })
1309
+ }
1310
+ )
1311
+ }
1312
+ );
1313
+ }
1314
+ );
1315
+
1316
+ // src/index.ts
1317
+ import { createRengeTheme as createRengeTheme2, PHI, FIBONACCI, GOLDEN_ANGLE, ANIMATION_NAMES } from "@renge-ui/tokens";
1318
+ export {
1319
+ ANIMATION_NAMES,
1320
+ Alert,
1321
+ Avatar,
1322
+ Badge,
1323
+ Button,
1324
+ Card,
1325
+ Chip,
1326
+ Divider,
1327
+ EnergyRing,
1328
+ FIBONACCI,
1329
+ FlowField,
1330
+ FormField,
1331
+ GOLDEN_ANGLE,
1332
+ Grid,
1333
+ Heading,
1334
+ Input,
1335
+ Navbar,
1336
+ PHI,
1337
+ Progress,
1338
+ Pulse,
1339
+ RengeProvider,
1340
+ Section,
1341
+ Spinner,
1342
+ Stack,
1343
+ Stat,
1344
+ Text,
1345
+ createRengeTheme2 as createRengeTheme,
1346
+ useRenge,
1347
+ useRengeTheme
1348
+ };
1349
+ //# sourceMappingURL=index.mjs.map