@waveso/ui 0.15.1 → 0.17.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 (87) hide show
  1. package/README.md +13 -12
  2. package/dist/accordion.js +2 -2
  3. package/dist/action-bar.js +4 -4
  4. package/dist/alert-dialog.js +5 -5
  5. package/dist/alert.d.ts +8 -1
  6. package/dist/alert.js +13 -4
  7. package/dist/animate.d.ts +1 -1
  8. package/dist/animate.js +2 -9
  9. package/dist/autocomplete.js +3 -3
  10. package/dist/avatar.d.ts +1 -1
  11. package/dist/avatar.js +4 -4
  12. package/dist/badge.d.ts +1 -1
  13. package/dist/badge.js +6 -6
  14. package/dist/button-group.d.ts +4 -2
  15. package/dist/button-group.js +5 -4
  16. package/dist/button.d.ts +1 -1
  17. package/dist/button.js +6 -6
  18. package/dist/card.d.ts +11 -4
  19. package/dist/card.js +27 -14
  20. package/dist/checkbox.js +2 -2
  21. package/dist/combobox.js +4 -4
  22. package/dist/context-menu.d.ts +2 -1
  23. package/dist/context-menu.js +14 -14
  24. package/dist/dialog.js +5 -5
  25. package/dist/drawer.js +1 -1
  26. package/dist/encrypted-text.d.ts +15 -1
  27. package/dist/encrypted-text.js +205 -67
  28. package/dist/field.d.ts +21 -3
  29. package/dist/field.js +36 -8
  30. package/dist/fieldset.d.ts +14 -0
  31. package/dist/fieldset.js +26 -0
  32. package/dist/form.d.ts +14 -14
  33. package/dist/form.js +7 -4
  34. package/dist/glyph-matrix.d.ts +25 -0
  35. package/dist/glyph-matrix.js +266 -0
  36. package/dist/gradient-reveal-text.d.ts +3 -3
  37. package/dist/gradient-reveal-text.js +8 -8
  38. package/dist/input-group.js +1 -1
  39. package/dist/input-otp.d.ts +3 -3
  40. package/dist/input-otp.js +2 -2
  41. package/dist/input.js +1 -1
  42. package/dist/item.d.ts +1 -1
  43. package/dist/item.js +17 -11
  44. package/dist/kbd.d.ts +1 -1
  45. package/dist/kbd.js +2 -2
  46. package/dist/label.js +1 -1
  47. package/dist/lib/merge-refs.d.ts +9 -0
  48. package/dist/lib/merge-refs.js +16 -0
  49. package/dist/lib/utils.js +7 -0
  50. package/dist/masonry.d.ts +1 -1
  51. package/dist/masonry.js +4 -2
  52. package/dist/menu.d.ts +2 -1
  53. package/dist/menu.js +7 -7
  54. package/dist/menubar.js +1 -1
  55. package/dist/pagination.js +1 -1
  56. package/dist/popover.js +1 -1
  57. package/dist/preview-card.js +1 -1
  58. package/dist/progress.js +2 -2
  59. package/dist/radio-group.js +1 -1
  60. package/dist/radio.js +2 -2
  61. package/dist/resizable.d.ts +43 -0
  62. package/dist/resizable.js +53 -0
  63. package/dist/scroll-area.d.ts +13 -2
  64. package/dist/scroll-area.js +26 -7
  65. package/dist/select.js +4 -4
  66. package/dist/separator.js +1 -1
  67. package/dist/sidebar.js +12 -12
  68. package/dist/slider.js +3 -3
  69. package/dist/spinner.d.ts +7 -0
  70. package/dist/spinner.js +24 -6
  71. package/dist/styles.css +581 -194
  72. package/dist/swipe-deck.d.ts +98 -0
  73. package/dist/swipe-deck.js +931 -0
  74. package/dist/switch.js +6 -6
  75. package/dist/table.d.ts +7 -1
  76. package/dist/table.js +40 -9
  77. package/dist/tabs.js +3 -3
  78. package/dist/textarea.js +1 -1
  79. package/dist/toast.js +4 -4
  80. package/dist/toggle-group.js +3 -3
  81. package/dist/toggle.d.ts +1 -1
  82. package/dist/toggle.js +11 -6
  83. package/dist/tooltip.d.ts +3 -1
  84. package/dist/tooltip.js +10 -6
  85. package/dist/video-text.d.ts +25 -0
  86. package/dist/video-text.js +147 -0
  87. package/package.json +6 -1
package/dist/field.js CHANGED
@@ -1,50 +1,78 @@
1
1
  "use client";
2
2
  import { cn } from "./lib/utils.js";
3
3
  import { jsx } from "react/jsx-runtime";
4
+ import { cva } from "class-variance-authority";
4
5
  import { Field as FieldPrimitive } from "@base-ui/react/field";
5
6
  //#region src/field.tsx
6
- function Field({ className, ...props }) {
7
+ const fieldVariants = cva("flex w-full gap-3", {
8
+ variants: { orientation: {
9
+ vertical: "flex-col *:w-full [&>.sr-only]:w-auto",
10
+ horizontal: "flex flex-row items-center *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px"
11
+ } },
12
+ defaultVariants: { orientation: "vertical" }
13
+ });
14
+ /**
15
+ * A labelled control with its messages. Below the control, `FieldError`
16
+ * comes first and `FieldDescription` under it, so the hint stays visible
17
+ * while the error is showing. An invalid field doesn't tint its text: the
18
+ * control's danger ring and the error carry the state, and the label
19
+ * stays neutral.
20
+ */
21
+ function Field({ className, orientation, ...props }) {
7
22
  return /* @__PURE__ */ jsx(FieldPrimitive.Root, {
8
23
  "data-slot": "field",
9
- className: cn("grid w-full gap-2 data-[invalid]:text-destructive", className),
24
+ "data-orientation": orientation ?? "vertical",
25
+ className: cn(fieldVariants({ orientation }), className),
10
26
  ...props
11
27
  });
12
28
  }
13
29
  function FieldLabel({ className, ...props }) {
14
30
  return /* @__PURE__ */ jsx(FieldPrimitive.Label, {
15
31
  "data-slot": "field-label",
16
- className: cn("text-sm font-medium leading-none data-[disabled]:cursor-not-allowed data-[disabled]:opacity-70", className),
32
+ className: cn("flex w-fit items-center gap-2 text-sm leading-snug font-medium select-none [&[for]]:cursor-clickable data-disabled:pointer-events-none data-disabled:opacity-30", className),
17
33
  ...props
18
34
  });
19
35
  }
20
36
  function FieldControl({ className, ...props }) {
21
37
  return /* @__PURE__ */ jsx(FieldPrimitive.Control, {
22
38
  "data-slot": "field-control",
23
- className: cn("data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50", className),
39
+ className,
40
+ ...props
41
+ });
42
+ }
43
+ /**
44
+ * A label and its description stacked together, beside a control. The label
45
+ * row is as tall as the default checkbox, radio and switch (24px), so its
46
+ * text centers on the control.
47
+ */
48
+ function FieldContent({ className, ...props }) {
49
+ return /* @__PURE__ */ jsx("div", {
50
+ "data-slot": "field-content",
51
+ className: cn("flex flex-1 flex-col gap-2 leading-snug [&>[data-slot=field-label]]:min-h-6", className),
24
52
  ...props
25
53
  });
26
54
  }
27
55
  function FieldDescription({ className, ...props }) {
28
56
  return /* @__PURE__ */ jsx(FieldPrimitive.Description, {
29
57
  "data-slot": "field-description",
30
- className: cn("text-muted text-left text-sm leading-normal font-normal", "[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4", className),
58
+ className: cn("text-muted text-start text-sm leading-normal font-normal", "[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4", "last:mt-0 nth-last-2:-mt-1 group-has-data-horizontal/field:text-balance", className),
31
59
  ...props
32
60
  });
33
61
  }
34
62
  function FieldItem({ className, ...props }) {
35
63
  return /* @__PURE__ */ jsx(FieldPrimitive.Item, {
36
64
  "data-slot": "field-item",
37
- className: cn("flex items-center gap-2", className),
65
+ className: cn("group/field flex items-center gap-2", className),
38
66
  ...props
39
67
  });
40
68
  }
41
69
  function FieldError({ className, ...props }) {
42
70
  return /* @__PURE__ */ jsx(FieldPrimitive.Error, {
43
71
  "data-slot": "field-error",
44
- className: cn("text-destructive text-sm font-normal", className),
72
+ className: cn("text-danger text-sm font-normal -mb-3", className),
45
73
  ...props
46
74
  });
47
75
  }
48
76
  const FieldValidity = FieldPrimitive.Validity;
49
77
  //#endregion
50
- export { Field, FieldControl, FieldDescription, FieldError, FieldItem, FieldLabel, FieldPrimitive, FieldValidity };
78
+ export { Field, FieldContent, FieldControl, FieldDescription, FieldError, FieldItem, FieldLabel, FieldPrimitive, FieldValidity, fieldVariants };
@@ -0,0 +1,14 @@
1
+ import * as React from "react";
2
+ import { Fieldset as Fieldset$1 } from "@base-ui/react/fieldset";
3
+ //#region src/fieldset.d.ts
4
+ type FieldsetProps = React.ComponentProps<typeof Fieldset$1.Root>;
5
+ type FieldsetLegendProps = React.ComponentProps<typeof Fieldset$1.Legend>;
6
+ /**
7
+ * Groups related fields under one legend (an address, a set of choices).
8
+ * `disabled` disables every `Field` inside it.
9
+ */
10
+ declare function Fieldset({ className, ...props }: FieldsetProps): React.JSX.Element;
11
+ /** The fieldset's title, announced as its accessible name. */
12
+ declare function FieldsetLegend({ className, ...props }: FieldsetLegendProps): React.JSX.Element;
13
+ //#endregion
14
+ export { Fieldset, FieldsetLegend, type FieldsetLegendProps, type FieldsetProps };
@@ -0,0 +1,26 @@
1
+ "use client";
2
+ import { cn } from "./lib/utils.js";
3
+ import { jsx } from "react/jsx-runtime";
4
+ import { Fieldset as Fieldset$1 } from "@base-ui/react/fieldset";
5
+ //#region src/fieldset.tsx
6
+ /**
7
+ * Groups related fields under one legend (an address, a set of choices).
8
+ * `disabled` disables every `Field` inside it.
9
+ */
10
+ function Fieldset({ className, ...props }) {
11
+ return /* @__PURE__ */ jsx(Fieldset$1.Root, {
12
+ "data-slot": "fieldset",
13
+ className: cn("flex w-full min-w-0 flex-col gap-7 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3 *:data-[slot=field-group]:gap-4", className),
14
+ ...props
15
+ });
16
+ }
17
+ /** The fieldset's title, announced as its accessible name. */
18
+ function FieldsetLegend({ className, ...props }) {
19
+ return /* @__PURE__ */ jsx(Fieldset$1.Legend, {
20
+ "data-slot": "fieldset-legend",
21
+ className: cn("mb-3 text-base font-medium data-disabled:opacity-30", className),
22
+ ...props
23
+ });
24
+ }
25
+ //#endregion
26
+ export { Fieldset, FieldsetLegend };
package/dist/form.d.ts CHANGED
@@ -7,20 +7,6 @@ type FormProps<TFieldValues extends FieldValues> = {
7
7
  onSubmit: SubmitHandler<TFieldValues>;
8
8
  } & Omit<React.ComponentProps<"form">, "onSubmit">;
9
9
  type FormRootProps = React.ComponentProps<"form">;
10
- type FormErrorProps = React.ComponentProps<"p">;
11
- type FormActionsProps = React.ComponentProps<"div">;
12
- type FormMessageProps = React.ComponentProps<"p">;
13
- declare function Form<TFieldValues extends FieldValues>({ form, onSubmit, ...props }: FormProps<TFieldValues>): React.JSX.Element;
14
- /**
15
- * Plain `<form>` element with RHF integration.
16
- *
17
- * Uses a native `<form>` instead of `@base-ui/react/form` because Base UI
18
- * Form's internal submit/validation handling conflicts with React Hook Form
19
- * and blocks button click events inside modal dialogs. Since RHF already
20
- * provides validation, error handling, and submit management, the Base UI
21
- * Form wrapper adds no value at the moment.
22
- */
23
- declare function FormRoot({ className, ...props }: FormRootProps): React.JSX.Element;
24
10
  type FormFieldRenderArgs<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValues>> = {
25
11
  id: string;
26
12
  field: ControllerRenderProps<TFieldValues, TName>;
@@ -43,6 +29,20 @@ type FormFieldProps<TFieldValues extends FieldValues, TName extends FieldPath<TF
43
29
  */
44
30
  render: (args: FormFieldRenderArgs<TFieldValues, TName>) => React.ReactNode;
45
31
  } & Omit<React.ComponentProps<typeof Field>, "children" | "name" | "invalid" | "touched" | "dirty" | "render">;
32
+ type FormErrorProps = React.ComponentProps<"p">;
33
+ type FormActionsProps = React.ComponentProps<"div">;
34
+ type FormMessageProps = React.ComponentProps<"p">;
35
+ declare function Form<TFieldValues extends FieldValues>({ form, onSubmit, ...props }: FormProps<TFieldValues>): React.JSX.Element;
36
+ /**
37
+ * Plain `<form>` element with RHF integration.
38
+ *
39
+ * Uses a native `<form>` instead of `@base-ui/react/form` because Base UI
40
+ * Form's internal submit/validation handling conflicts with React Hook Form
41
+ * and blocks button click events inside modal dialogs. Since RHF already
42
+ * provides validation, error handling, and submit management, the Base UI
43
+ * Form wrapper adds no value at the moment.
44
+ */
45
+ declare function FormRoot({ className, ...props }: FormRootProps): React.JSX.Element;
46
46
  /**
47
47
  * Base UI + React Hook Form field helper.
48
48
  *
package/dist/form.js CHANGED
@@ -65,16 +65,19 @@ function FormField(props) {
65
65
  children: [
66
66
  label ? /* @__PURE__ */ jsxs(FieldLabel, { children: [label, isRequired ? /* @__PURE__ */ jsx("span", {
67
67
  "aria-hidden": "true",
68
- className: "text-destructive ml-1",
68
+ className: "text-danger ms-1",
69
69
  children: "*"
70
70
  }) : null] }) : null,
71
- description ? /* @__PURE__ */ jsx(FieldDescription, { children: description }) : null,
72
71
  render({
73
72
  id: controlId,
74
73
  field,
75
74
  fieldState
76
75
  }),
77
- fieldState.error ? /* @__PURE__ */ jsx(FieldError, { children: fieldState.error.message }) : null
76
+ fieldState.error ? /* @__PURE__ */ jsx(FieldError, {
77
+ match: true,
78
+ children: fieldState.error.message
79
+ }) : null,
80
+ description ? /* @__PURE__ */ jsx(FieldDescription, { children: description }) : null
78
81
  ]
79
82
  })
80
83
  });
@@ -86,7 +89,7 @@ function FormError({ className, ...props }) {
86
89
  return /* @__PURE__ */ jsx("p", {
87
90
  "data-slot": "form-error",
88
91
  role: "alert",
89
- className: cn("text-destructive rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-sm", className),
92
+ className: cn("text-danger rounded-lg border border-danger/30 bg-danger/5 p-4 text-sm", className),
90
93
  ...props,
91
94
  children: message
92
95
  });
@@ -0,0 +1,25 @@
1
+ import * as React from "react";
2
+ //#region src/glyph-matrix.d.ts
3
+ interface GlyphMatrixProps extends React.ComponentProps<"canvas"> {
4
+ /** Characters picked from at random. */
5
+ glyphs?: string;
6
+ /** Multiplies how fast the field changes. 0 holds it still. */
7
+ speed?: number;
8
+ /**
9
+ * Bright flashes, some typing short runs along a row. Off leaves a calm
10
+ * shimmer: glyphs swapping quietly at their resting brightness.
11
+ */
12
+ glitch?: boolean;
13
+ }
14
+ /**
15
+ * A field of glitching glyphs, for decorative backgrounds (`glitch={false}`
16
+ * for a calm shimmer behind busy content). Styled with
17
+ * classes: `text-*` sets the glyphs' color and size, `font-*` their typeface,
18
+ * and `accent-*` the color of the flares, which alternate with the text color
19
+ * at full strength. A theme switch re-tints the field. `opacity-*` makes it
20
+ * fainter, and `mask-*` fades it toward an edge (`mask-b-from-40%`) or around
21
+ * content on top (`mask-radial-from-transparent mask-radial-to-black`).
22
+ */
23
+ declare function GlyphMatrix({ glyphs, speed, glitch, className, ref, ...props }: GlyphMatrixProps): React.JSX.Element;
24
+ //#endregion
25
+ export { GlyphMatrix, type GlyphMatrixProps };
@@ -0,0 +1,266 @@
1
+ "use client";
2
+ import { cn } from "./lib/utils.js";
3
+ import { mergeRefs } from "./lib/merge-refs.js";
4
+ import { jsx } from "react/jsx-runtime";
5
+ import * as React from "react";
6
+ //#region src/glyph-matrix.tsx
7
+ const DEFAULT_GLYPHS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$&*()-_+=/[]{};:<>.,0123456789";
8
+ /** Space beside each glyph and the row height, in ems. */
9
+ const CELL_GAP = .6;
10
+ const ROW_HEIGHT = 1.55;
11
+ /** Share of cells left blank. */
12
+ const BLANK_SHARE = .1;
13
+ /** Resting opacity range of the other cells. */
14
+ const REST_MIN = .12;
15
+ const REST_RANGE = .38;
16
+ /** Share of the field that quietly swaps its glyph each second. */
17
+ const SWAP_RATE = .2;
18
+ /** Share of the field that flares each second. */
19
+ const FLARE_RATE = .035;
20
+ /** Chance a flare starts a streak: a run that types along its row. */
21
+ const STREAK_CHANCE = .24;
22
+ /** Share of streaks that run long (10–24 cells instead of 2–8). */
23
+ const LONG_STREAK_CHANCE = 1 / 16;
24
+ /** Seconds between a streak's cells. */
25
+ const STREAK_PACE = .028;
26
+ /** Faint background glyphs gain nothing from more than 2 device pixels per px. */
27
+ const MAX_PIXEL_RATIO = 2;
28
+ /** How often the canvas's color and font are re-read, in seconds. */
29
+ const STYLE_CHECK = .25;
30
+ function randomGlyph(pool) {
31
+ return pool[Math.floor(Math.random() * pool.length)] ?? " ";
32
+ }
33
+ function randomRest() {
34
+ return Math.random() < BLANK_SHARE ? 0 : REST_MIN + Math.random() * REST_RANGE;
35
+ }
36
+ function randomFlare() {
37
+ return {
38
+ peak: .45 + Math.random() * .55,
39
+ hold: .04 + Math.random() * .26,
40
+ fade: .45 + Math.random() * .6,
41
+ accent: Math.random() < .5
42
+ };
43
+ }
44
+ /** Cells a streak adds after its first one. */
45
+ function streakLength() {
46
+ return Math.random() < LONG_STREAK_CHANCE ? 9 + Math.floor(Math.random() * 15) : 1 + Math.floor(Math.random() * 7);
47
+ }
48
+ /**
49
+ * A field of glitching glyphs, for decorative backgrounds (`glitch={false}`
50
+ * for a calm shimmer behind busy content). Styled with
51
+ * classes: `text-*` sets the glyphs' color and size, `font-*` their typeface,
52
+ * and `accent-*` the color of the flares, which alternate with the text color
53
+ * at full strength. A theme switch re-tints the field. `opacity-*` makes it
54
+ * fainter, and `mask-*` fades it toward an edge (`mask-b-from-40%`) or around
55
+ * content on top (`mask-radial-from-transparent mask-radial-to-black`).
56
+ */
57
+ function GlyphMatrix({ glyphs = DEFAULT_GLYPHS, speed = 1, glitch = true, className, ref, ...props }) {
58
+ const canvasRef = React.useRef(null);
59
+ const mergedRef = React.useMemo(() => mergeRefs(canvasRef, ref), [ref]);
60
+ React.useEffect(() => {
61
+ const canvas = canvasRef.current;
62
+ const context = canvas?.getContext("2d");
63
+ if (!canvas || !context) return;
64
+ const pool = Array.from(glyphs.length > 0 ? glyphs : DEFAULT_GLYPHS);
65
+ const pace = Number.isFinite(speed) ? Math.max(0, speed) : 1;
66
+ const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
67
+ let color = "";
68
+ let accent = "";
69
+ let font = "";
70
+ let cellWidth = 0;
71
+ let cellHeight = 0;
72
+ let cols = 0;
73
+ let total = 0;
74
+ let cells = [];
75
+ let rests = /* @__PURE__ */ new Float32Array(0);
76
+ let heats = /* @__PURE__ */ new Float32Array(0);
77
+ let flares = [];
78
+ const active = /* @__PURE__ */ new Set();
79
+ const dirty = /* @__PURE__ */ new Set();
80
+ const streaks = [];
81
+ let swapCarry = 0;
82
+ let flareCarry = 0;
83
+ let styleCheck = STYLE_CHECK;
84
+ let visible = true;
85
+ let disposed = false;
86
+ let frame = 0;
87
+ let last = 0;
88
+ const drawCell = (index) => {
89
+ const x = index % cols * cellWidth;
90
+ const y = Math.floor(index / cols) * cellHeight;
91
+ const glyph = cells[index] ?? " ";
92
+ const rest = rests[index] ?? 0;
93
+ const heat = heats[index] ?? 0;
94
+ context.clearRect(x, y, cellWidth, cellHeight);
95
+ if (rest * (1 - heat) > .004) {
96
+ context.fillStyle = color;
97
+ context.globalAlpha = rest * (1 - heat);
98
+ context.fillText(glyph, x + cellWidth / 2, y + cellHeight / 2);
99
+ }
100
+ if (heat > .004) {
101
+ context.fillStyle = flares[index]?.accent ? accent : color;
102
+ context.globalAlpha = heat;
103
+ context.fillText(glyph, x + cellWidth / 2, y + cellHeight / 2);
104
+ }
105
+ };
106
+ const paintAll = () => {
107
+ context.clearRect(0, 0, canvas.clientWidth, canvas.clientHeight);
108
+ context.font = font;
109
+ context.textAlign = "center";
110
+ context.textBaseline = "middle";
111
+ for (let index = 0; index < total; index++) drawCell(index);
112
+ dirty.clear();
113
+ };
114
+ const build = () => {
115
+ const ratio = Math.min(window.devicePixelRatio || 1, MAX_PIXEL_RATIO);
116
+ const { clientWidth: width, clientHeight: height } = canvas;
117
+ canvas.width = Math.round(width * ratio);
118
+ canvas.height = Math.round(height * ratio);
119
+ context.setTransform(ratio, 0, 0, ratio, 0, 0);
120
+ context.font = font;
121
+ const size = parseFloat(getComputedStyle(canvas).fontSize) || 14;
122
+ let advance = 1;
123
+ for (const glyph of new Set(pool)) advance = Math.max(advance, context.measureText(glyph).width);
124
+ cellWidth = Math.ceil(advance + size * CELL_GAP);
125
+ cellHeight = Math.ceil(size * ROW_HEIGHT);
126
+ cols = Math.ceil(width / cellWidth);
127
+ total = cols * Math.ceil(height / cellHeight);
128
+ cells = Array.from({ length: total }, () => randomGlyph(pool));
129
+ rests = Float32Array.from({ length: total }, randomRest);
130
+ heats = new Float32Array(total);
131
+ flares = [];
132
+ active.clear();
133
+ dirty.clear();
134
+ streaks.length = 0;
135
+ paintAll();
136
+ };
137
+ const syncStyle = () => {
138
+ const computed = getComputedStyle(canvas);
139
+ const nextAccent = computed.accentColor === "auto" ? computed.color : computed.accentColor;
140
+ const nextFont = `${computed.fontWeight} ${computed.fontSize} ${computed.fontFamily}`;
141
+ const colorChanged = computed.color !== color || nextAccent !== accent;
142
+ color = computed.color;
143
+ accent = nextAccent;
144
+ if (nextFont !== font) {
145
+ font = nextFont;
146
+ build();
147
+ if (!document.fonts.check(nextFont)) document.fonts.load(nextFont).then(() => {
148
+ if (!disposed && font === nextFont) build();
149
+ }, () => {});
150
+ } else if (colorChanged) paintAll();
151
+ };
152
+ const ignite = (index, flare) => {
153
+ flares[index] = { ...flare };
154
+ heats[index] = flare.peak;
155
+ active.add(index);
156
+ dirty.add(index);
157
+ };
158
+ const update = (dt) => {
159
+ swapCarry += total * SWAP_RATE * pace * dt;
160
+ for (; swapCarry >= 1; swapCarry--) {
161
+ const index = Math.floor(Math.random() * total);
162
+ if (active.has(index)) continue;
163
+ cells[index] = randomGlyph(pool);
164
+ dirty.add(index);
165
+ }
166
+ if (glitch) flareCarry += total * FLARE_RATE * pace * dt;
167
+ for (; flareCarry >= 1; flareCarry--) {
168
+ const index = Math.floor(Math.random() * total);
169
+ const flare = randomFlare();
170
+ ignite(index, flare);
171
+ if (Math.random() < STREAK_CHANCE) streaks.push({
172
+ index,
173
+ left: streakLength(),
174
+ carry: 0,
175
+ flare
176
+ });
177
+ }
178
+ for (let s = streaks.length - 1; s >= 0; s--) {
179
+ const streak = streaks[s];
180
+ if (!streak) continue;
181
+ streak.carry += dt * pace / STREAK_PACE;
182
+ while (streak.carry >= 1 && streak.left > 0) {
183
+ streak.carry -= 1;
184
+ streak.left -= 1;
185
+ streak.index += 1;
186
+ if (streak.index % cols === 0 || streak.index >= total) {
187
+ streak.left = 0;
188
+ break;
189
+ }
190
+ ignite(streak.index, streak.flare);
191
+ }
192
+ if (streak.left <= 0) streaks.splice(s, 1);
193
+ }
194
+ for (const index of active) {
195
+ const flare = flares[index];
196
+ if (!flare) {
197
+ active.delete(index);
198
+ continue;
199
+ }
200
+ if (flare.hold > 0) flare.hold -= dt;
201
+ else heats[index] = (heats[index] ?? 0) * Math.exp(-dt * 4 / flare.fade);
202
+ const heat = heats[index] ?? 0;
203
+ if (heat < .012 && flare.hold <= 0) {
204
+ heats[index] = 0;
205
+ active.delete(index);
206
+ } else if (Math.random() < heat * dt * 12) cells[index] = randomGlyph(pool);
207
+ dirty.add(index);
208
+ }
209
+ for (const index of dirty) drawCell(index);
210
+ dirty.clear();
211
+ };
212
+ const settle = () => {
213
+ for (const index of active) {
214
+ heats[index] = 0;
215
+ drawCell(index);
216
+ }
217
+ active.clear();
218
+ streaks.length = 0;
219
+ };
220
+ const tick = (time) => {
221
+ frame = requestAnimationFrame(tick);
222
+ const dt = last ? Math.min(.05, (time - last) / 1e3) : 0;
223
+ last = time;
224
+ if (!visible || dt === 0) return;
225
+ styleCheck -= dt;
226
+ if (styleCheck <= 0) {
227
+ styleCheck = STYLE_CHECK;
228
+ syncStyle();
229
+ }
230
+ if (reducedMotion.matches) {
231
+ if (active.size > 0) settle();
232
+ return;
233
+ }
234
+ update(dt);
235
+ };
236
+ const resizeObserver = new ResizeObserver(() => {
237
+ if (font) build();
238
+ else syncStyle();
239
+ });
240
+ resizeObserver.observe(canvas);
241
+ const intersectionObserver = new IntersectionObserver(([entry]) => {
242
+ if (entry) visible = entry.isIntersecting;
243
+ });
244
+ intersectionObserver.observe(canvas);
245
+ frame = requestAnimationFrame(tick);
246
+ return () => {
247
+ disposed = true;
248
+ cancelAnimationFrame(frame);
249
+ resizeObserver.disconnect();
250
+ intersectionObserver.disconnect();
251
+ };
252
+ }, [
253
+ glyphs,
254
+ speed,
255
+ glitch
256
+ ]);
257
+ return /* @__PURE__ */ jsx("canvas", {
258
+ ref: mergedRef,
259
+ "data-slot": "glyph-matrix",
260
+ "aria-hidden": "true",
261
+ className: cn("text-contrast accent-accent font-mono text-sm pointer-events-none block size-full", className),
262
+ ...props
263
+ });
264
+ }
265
+ //#endregion
266
+ export { GlyphMatrix };
@@ -4,7 +4,7 @@ interface GradientRevealTextProps {
4
4
  text: string;
5
5
  /** Spotlight follow speed in seconds. Default: 0 (instant) */
6
6
  duration?: number;
7
- /** Gradient colors for the reveal effect. Default: rainbow */
7
+ /** Gradient colors for the reveal effect, any CSS color (`var(--wave-teal-400)` too). Default: rainbow */
8
8
  colors?: string[];
9
9
  /** Base stroke opacity when not hovered. Default: 0.3 */
10
10
  baseOpacity?: number;
@@ -16,7 +16,7 @@ interface GradientRevealTextProps {
16
16
  spotlightSize?: number;
17
17
  /** Stroke width in px. Default: auto (1.5% of text height) */
18
18
  strokeWidth?: number;
19
- /** Base stroke color. Default: neutral-200 (light) / neutral-800 (dark) via Tailwind */
19
+ /** Base stroke color. Default: the `contour` token, so it follows the theme */
20
20
  baseColor?: string;
21
21
  className?: string;
22
22
  }
@@ -30,7 +30,7 @@ interface GradientRevealTextProps {
30
30
  * @example
31
31
  * ```tsx
32
32
  * <GradientRevealText text="HELLO" />
33
- * <GradientRevealText text="BRAND" colors={["#ff0000", "#00ff00"]} />
33
+ * <GradientRevealText text="BRAND" colors={["var(--wave-red-400)", "var(--wave-green-400)"]} />
34
34
  * ```
35
35
  */
36
36
  declare function GradientRevealText({ text, duration, colors, baseOpacity, hoverOpacity, fontFamily, spotlightSize, strokeWidth: strokeWidthPx, baseColor, className }: GradientRevealTextProps): import("react").JSX.Element;
@@ -4,11 +4,11 @@ import { jsx, jsxs } from "react/jsx-runtime";
4
4
  import { useCallback, useEffect, useId, useRef, useState } from "react";
5
5
  //#region src/gradient-reveal-text.tsx
6
6
  const DEFAULT_COLORS = [
7
- "#eab308",
8
- "#ef4444",
9
- "#3b82f6",
10
- "#06b6d4",
11
- "#8b5cf6"
7
+ "var(--wave-yellow-400)",
8
+ "var(--wave-red-400)",
9
+ "var(--wave-blue-400)",
10
+ "var(--wave-teal-400)",
11
+ "var(--wave-purple-400)"
12
12
  ];
13
13
  /**
14
14
  * Large decorative text with a gradient spotlight that follows the cursor.
@@ -20,7 +20,7 @@ const DEFAULT_COLORS = [
20
20
  * @example
21
21
  * ```tsx
22
22
  * <GradientRevealText text="HELLO" />
23
- * <GradientRevealText text="BRAND" colors={["#ff0000", "#00ff00"]} />
23
+ * <GradientRevealText text="BRAND" colors={["var(--wave-red-400)", "var(--wave-green-400)"]} />
24
24
  * ```
25
25
  */
26
26
  function GradientRevealText({ text, duration = 0, colors = DEFAULT_COLORS, baseOpacity = .3, hoverOpacity = .7, fontFamily = "Helvetica Neue, Helvetica, Arial, sans-serif", spotlightSize = .6, strokeWidth: strokeWidthPx, baseColor, className }) {
@@ -188,7 +188,7 @@ function GradientRevealText({ text, duration = 0, colors = DEFAULT_COLORS, baseO
188
188
  y2: "0%",
189
189
  children: hovered && stops.map((s) => /* @__PURE__ */ jsx("stop", {
190
190
  offset: s.offset,
191
- stopColor: s.color
191
+ style: { stopColor: s.color }
192
192
  }, s.offset))
193
193
  }),
194
194
  /* @__PURE__ */ jsxs("radialGradient", {
@@ -236,7 +236,7 @@ function GradientRevealText({ text, duration = 0, colors = DEFAULT_COLORS, baseO
236
236
  y: "50%",
237
237
  textAnchor: "middle",
238
238
  dominantBaseline: "central",
239
- className: baseColor ? "font-bold" : "font-bold stroke-neutral-200 dark:stroke-neutral-800",
239
+ className: baseColor ? "font-bold" : "font-bold stroke-contour",
240
240
  style: {
241
241
  ...textStyle,
242
242
  ...baseColor ? { stroke: baseColor } : {},
@@ -6,7 +6,7 @@ import { Textarea } from "./textarea.js";
6
6
  import { jsx } from "react/jsx-runtime";
7
7
  import { cva } from "class-variance-authority";
8
8
  //#region src/input-group.tsx
9
- const inputGroupVariants = cva("bg-edge hover:border-focus has-[[data-slot=input-group-control]:focus-visible]:border-focus has-[[data-slot=input-group-control]:focus-visible]:ring-focus/50 has-[[data-slot][aria-invalid=true]]:ring-destructive/30 has-[[data-slot][aria-invalid=true]]:border-destructive group/input-group relative flex w-full min-w-0 items-center rounded-md border border-transparent motion-color outline-hidden in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:pointer-events-none has-disabled:opacity-30 not-has-disabled:has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-3 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3", {
9
+ const inputGroupVariants = cva("bg-shade hover:border-focus has-[[data-slot=input-group-control]:focus-visible]:border-focus has-[[data-slot=input-group-control]:focus-visible]:ring-focus/50 has-[[data-slot][aria-invalid=true]]:ring-danger/30 has-[[data-slot][aria-invalid=true]]:border-danger group/input-group relative flex w-full min-w-0 items-center rounded-md border border-transparent motion-color outline-hidden in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-disabled:pointer-events-none has-disabled:opacity-30 not-has-disabled:has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-3 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3", {
10
10
  variants: { size: {
11
11
  xs: "h-6 rounded-sm",
12
12
  sm: "h-8",
@@ -4,14 +4,14 @@ import { OTPInput } from "input-otp";
4
4
  type InputOTPProps = React.ComponentProps<typeof OTPInput> & {
5
5
  containerClassName?: string;
6
6
  };
7
- declare function InputOTP({ className, containerClassName, ...props }: InputOTPProps): React.JSX.Element;
8
7
  type InputOTPGroupProps = React.ComponentProps<"div">;
9
- declare function InputOTPGroup({ className, ...props }: InputOTPGroupProps): React.JSX.Element;
10
8
  type InputOTPSlotProps = React.ComponentProps<"div"> & {
11
9
  index: number;
12
10
  };
13
- declare function InputOTPSlot({ index, className, ...props }: InputOTPSlotProps): React.JSX.Element;
14
11
  type InputOTPSeparatorProps = React.ComponentProps<"div">;
12
+ declare function InputOTP({ className, containerClassName, ...props }: InputOTPProps): React.JSX.Element;
13
+ declare function InputOTPGroup({ className, ...props }: InputOTPGroupProps): React.JSX.Element;
14
+ declare function InputOTPSlot({ index, className, ...props }: InputOTPSlotProps): React.JSX.Element;
15
15
  declare function InputOTPSeparator({ className, ...props }: InputOTPSeparatorProps): React.JSX.Element;
16
16
  //#endregion
17
17
  export { InputOTP, InputOTPGroup, type InputOTPGroupProps, type InputOTPProps, InputOTPSeparator, type InputOTPSeparatorProps, InputOTPSlot, type InputOTPSlotProps };
package/dist/input-otp.js CHANGED
@@ -10,7 +10,7 @@ function InputOTP({ className, containerClassName, ...props }) {
10
10
  "data-slot": "input-otp",
11
11
  containerClassName: cn("group/input-otp flex items-center has-disabled:opacity-50", containerClassName),
12
12
  spellCheck: false,
13
- className: cn("disabled:cursor-not-allowed", className),
13
+ className: cn(className),
14
14
  ...props
15
15
  });
16
16
  }
@@ -26,7 +26,7 @@ function InputOTPSlot({ index, className, ...props }) {
26
26
  return /* @__PURE__ */ jsxs("div", {
27
27
  "data-slot": "input-otp-slot",
28
28
  "data-active": isActive || void 0,
29
- className: cn("border-edge dark:bg-edge/30 relative flex size-9 items-center justify-center border-y border-r text-sm motion-color outline-hidden first:rounded-l-md first:border-l last:rounded-r-md", "data-active:border-focus data-active:ring-3 data-active:ring-focus/50 data-active:z-10", "group-has-aria-invalid/input-otp:border-destructive group-has-aria-invalid/input-otp:data-active:ring-3 group-has-aria-invalid/input-otp:data-active:ring-destructive/30", className),
29
+ className: cn("border-shade relative flex size-9 items-center justify-center border-y border-r text-sm motion-color outline-hidden first:rounded-l-md first:border-l last:rounded-r-md", "data-active:border-focus data-active:ring-3 data-active:ring-focus/50 data-active:z-10", "group-has-aria-invalid/input-otp:border-danger group-has-aria-invalid/input-otp:data-active:ring-3 group-has-aria-invalid/input-otp:data-active:ring-danger/30", className),
30
30
  ...props,
31
31
  children: [char, hasFakeCaret && /* @__PURE__ */ jsx("div", {
32
32
  className: "pointer-events-none absolute inset-0 flex items-center justify-center",
package/dist/input.js CHANGED
@@ -3,7 +3,7 @@ import { jsx } from "react/jsx-runtime";
3
3
  import { cva } from "class-variance-authority";
4
4
  import { Input as Input$1 } from "@base-ui/react/input";
5
5
  //#region src/input.tsx
6
- const inputVariants = cva("bg-edge hover:border-focus focus-visible:border-focus focus-visible:ring-focus/50 aria-invalid:ring-destructive/30 aria-invalid:border-destructive placeholder:text-soft flex w-full min-w-0 items-center overflow-hidden rounded-md border border-transparent motion-color outline-hidden file:inline-flex file:my-[3px] file:items-center file:me-3 file:cursor-clickable file:rounded-md file:border-0 file:bg-primary file:px-2.5 file:leading-none file:font-medium file:text-white file:motion-color file:hover:bg-primary/80 focus-visible:ring-3 disabled:pointer-events-none disabled:opacity-30 aria-invalid:ring-3", {
6
+ const inputVariants = cva("bg-shade hover:border-focus focus-visible:border-focus focus-visible:ring-focus/50 aria-invalid:ring-danger/30 aria-invalid:border-danger placeholder:text-soft flex w-full min-w-0 items-center overflow-hidden rounded-md border border-transparent motion-color outline-hidden file:inline-flex file:my-[3px] file:items-center file:me-3 file:cursor-clickable file:rounded-md file:border-0 file:bg-primary file:px-2.5 file:leading-none file:font-medium file:text-white file:motion-color file:hover:bg-primary/80 focus-visible:ring-3 data-disabled:pointer-events-none data-disabled:opacity-30 aria-invalid:ring-3", {
7
7
  variants: { size: {
8
8
  xs: "h-6 rounded-sm px-2 text-xs file:-ms-[5px] file:h-4",
9
9
  sm: "h-8 px-3 text-sm file:-ms-[9px] file:h-6",
package/dist/item.d.ts CHANGED
@@ -25,7 +25,7 @@ declare function ItemSeparator({ className, ...props }: ItemSeparatorProps): Rea
25
25
  declare function Item({ className, variant, size, render, ...props }: ItemProps): React.ReactElement<unknown, string | React.JSXElementConstructor<any>>;
26
26
  declare function ItemMedia({ className, variant, ...props }: ItemMediaProps): React.JSX.Element;
27
27
  declare function ItemContent({ className, ...props }: ItemContentProps): React.JSX.Element;
28
- declare function ItemTitle({ className, ...props }: ItemTitleProps): React.JSX.Element;
28
+ declare function ItemTitle({ className, children, ...props }: ItemTitleProps): React.JSX.Element;
29
29
  declare function ItemDescription({ className, ...props }: ItemDescriptionProps): React.JSX.Element;
30
30
  declare function ItemActions({ className, ...props }: ItemActionsProps): React.JSX.Element;
31
31
  declare function ItemHeader({ className, ...props }: ItemHeaderProps): React.JSX.Element;