@claralight-design/react 0.0.1

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 (45) hide show
  1. package/dist/index.d.ts +12 -0
  2. package/dist/index.js +12 -0
  3. package/dist/lib/anchored.d.ts +152 -0
  4. package/dist/lib/anchored.js +821 -0
  5. package/dist/lib/anchored.js.map +1 -0
  6. package/dist/lib/morph.js +484 -0
  7. package/dist/lib/morph.js.map +1 -0
  8. package/dist/lib/squircle.d.ts +95 -0
  9. package/dist/lib/squircle.js +220 -0
  10. package/dist/lib/squircle.js.map +1 -0
  11. package/dist/lib/utils.d.ts +30 -0
  12. package/dist/lib/utils.js +76 -0
  13. package/dist/lib/utils.js.map +1 -0
  14. package/dist/ui/button.d.ts +40 -0
  15. package/dist/ui/button.js +104 -0
  16. package/dist/ui/button.js.map +1 -0
  17. package/dist/ui/card.d.ts +54 -0
  18. package/dist/ui/card.js +82 -0
  19. package/dist/ui/card.js.map +1 -0
  20. package/dist/ui/dialog.d.ts +82 -0
  21. package/dist/ui/dialog.js +129 -0
  22. package/dist/ui/dialog.js.map +1 -0
  23. package/dist/ui/input.d.ts +34 -0
  24. package/dist/ui/input.js +60 -0
  25. package/dist/ui/input.js.map +1 -0
  26. package/dist/ui/popover.d.ts +67 -0
  27. package/dist/ui/popover.js +93 -0
  28. package/dist/ui/popover.js.map +1 -0
  29. package/dist/ui/scroll-area.d.ts +110 -0
  30. package/dist/ui/scroll-area.js +125 -0
  31. package/dist/ui/scroll-area.js.map +1 -0
  32. package/dist/ui/select.d.ts +113 -0
  33. package/dist/ui/select.js +213 -0
  34. package/dist/ui/select.js.map +1 -0
  35. package/dist/ui/tooltip.d.ts +108 -0
  36. package/dist/ui/tooltip.js +142 -0
  37. package/dist/ui/tooltip.js.map +1 -0
  38. package/package.json +72 -0
  39. package/styles/anchored.css +115 -0
  40. package/styles/base.css +309 -0
  41. package/styles/fonts/README.md +63 -0
  42. package/styles/index.css +28 -0
  43. package/styles/scroll-area.css +299 -0
  44. package/styles/theme.css +540 -0
  45. package/styles/tooltip.css +156 -0
@@ -0,0 +1,213 @@
1
+ "use client";
2
+ import { cn } from "../lib/utils.js";
3
+ import { Squircle } from "../lib/squircle.js";
4
+ import { useRef } from "react";
5
+ import { jsx } from "react/jsx-runtime";
6
+ import { Select } from "@base-ui/react/select";
7
+ //#region src/ui/select.tsx
8
+ /**
9
+ * Inlined rather than pulled from an icon library: three glyphs is not worth a
10
+ * dependency in a component consumers copy into their own project, and swapping
11
+ * `lucide-react` in later is a one-line change per icon via `components.json`.
12
+ */
13
+ function ChevronDownIcon({ className, ...props }) {
14
+ return /* @__PURE__ */ jsx("svg", {
15
+ viewBox: "0 0 16 16",
16
+ fill: "none",
17
+ stroke: "currentColor",
18
+ strokeWidth: 1.6,
19
+ strokeLinecap: "round",
20
+ strokeLinejoin: "round",
21
+ "aria-hidden": "true",
22
+ className,
23
+ ...props,
24
+ children: /* @__PURE__ */ jsx("path", { d: "m4 6.5 4 4 4-4" })
25
+ });
26
+ }
27
+ function CheckIcon({ className, ...props }) {
28
+ return /* @__PURE__ */ jsx("svg", {
29
+ viewBox: "0 0 16 16",
30
+ fill: "none",
31
+ stroke: "currentColor",
32
+ strokeWidth: 1.8,
33
+ strokeLinecap: "round",
34
+ strokeLinejoin: "round",
35
+ "aria-hidden": "true",
36
+ className,
37
+ ...props,
38
+ children: /* @__PURE__ */ jsx("path", { d: "m3.5 8.5 3 3 6-7" })
39
+ });
40
+ }
41
+ const Select$1 = Select.Root;
42
+ /** Re-exported unstyled, for building something other than the default content. */
43
+ const SelectPortal = Select.Portal;
44
+ const SelectPositioner = Select.Positioner;
45
+ const SelectPopup = Select.Popup;
46
+ const SelectList = Select.List;
47
+ const triggerSizes = {
48
+ sm: "h-control-sm px-2 text-caption",
49
+ md: "h-control-md px-3 text-callout",
50
+ lg: "h-control-lg px-4 text-body"
51
+ };
52
+ const pressScales = {
53
+ sm: "[--cl-press-scale:var(--cl-press-scale-sm)]",
54
+ md: "[--cl-press-scale:var(--cl-press-scale-md)]",
55
+ lg: "[--cl-press-scale:var(--cl-press-scale-lg)]"
56
+ };
57
+ function SelectTrigger({ className, wrapperClassName, size = "md", ...props }) {
58
+ return /* @__PURE__ */ jsx(Squircle, {
59
+ asChild: true,
60
+ radius: "control",
61
+ ring: "field",
62
+ wrapperClassName: cn("cl-press w-full", pressScales[size], wrapperClassName),
63
+ children: /* @__PURE__ */ jsx(Select.Trigger, {
64
+ "data-cl-slot": "select-trigger",
65
+ className: cn("group flex w-full items-center justify-between gap-2", "border border-outline bg-control text-left font-sans text-foreground", "hover:bg-control-highlight", "data-[popup-open]:bg-control-highlight", "data-[disabled]:pointer-events-none data-[disabled]:text-foreground-disabled", "data-[placeholder]:text-foreground-hint", triggerSizes[size], className),
66
+ ...props
67
+ })
68
+ });
69
+ }
70
+ function SelectValue({ className, ...props }) {
71
+ return /* @__PURE__ */ jsx(Select.Value, {
72
+ "data-cl-slot": "select-value",
73
+ className: cn("truncate", className),
74
+ ...props
75
+ });
76
+ }
77
+ /**
78
+ * Flips with the popup and is `aria-hidden` because Base UI already exposes the
79
+ * expanded state; the glyph is decoration, not information.
80
+ */
81
+ function SelectIcon({ className, ...props }) {
82
+ return /* @__PURE__ */ jsx(Select.Icon, {
83
+ "data-cl-slot": "select-icon",
84
+ className: cn("shrink-0 text-foreground-tertiary", "transition-transform duration-(--cl-duration-fast) ease-cl-out", "group-data-[popup-open]:rotate-180", "[&_svg]:size-4", className),
85
+ ...props,
86
+ children: /* @__PURE__ */ jsx(ChevronDownIcon, {})
87
+ });
88
+ }
89
+ /**
90
+ * Portal, positioner and popup in the composition everyone actually wants.
91
+ *
92
+ * Two details come straight from Base UI:
93
+ *
94
+ * - `origin-[var(--transform-origin)]` — the positioner computes where the
95
+ * popup should *appear to grow from* based on the trigger's position, so the
96
+ * scale-in reads as the trigger unfolding rather than a box fading in.
97
+ * - `--available-height` / `--anchor-width` — measured space, so the list never
98
+ * spills past the viewport and the popup can match the trigger's width.
99
+ */
100
+ function SelectContent({ className, wrapperClassName, children, sideOffset, side, align = "start", ...props }) {
101
+ const positionerRef = useRef(null);
102
+ const themedSideOffset = () => {
103
+ const element = positionerRef.current;
104
+ if (!element) return 0;
105
+ const value = element.ownerDocument.defaultView?.getComputedStyle(element).getPropertyValue("--cl-select-side-offset");
106
+ return Number.parseFloat(value ?? "") || 0;
107
+ };
108
+ return /* @__PURE__ */ jsx(Select.Portal, { children: /* @__PURE__ */ jsx(Select.Positioner, {
109
+ ref: positionerRef,
110
+ sideOffset: sideOffset ?? themedSideOffset,
111
+ side,
112
+ align,
113
+ className: "z-50 outline-none select-none",
114
+ children: /* @__PURE__ */ jsx(Squircle, {
115
+ asChild: true,
116
+ radius: "medium",
117
+ wrapperClassName: cn("cl-enter-root origin-[var(--transform-origin)] w-fit", "min-w-[var(--anchor-width)] max-w-[min(var(--cl-select-max-width),var(--available-width))]", wrapperClassName),
118
+ children: /* @__PURE__ */ jsx(Select.Popup, {
119
+ "data-cl-slot": "select-content",
120
+ className: cn("cl-exit-sentinel cl-frost p-1 font-sans text-foreground", className),
121
+ ...props,
122
+ children: /* @__PURE__ */ jsx(Select.List, {
123
+ className: "max-h-[min(var(--available-height),var(--cl-select-max-height))] overflow-y-auto overscroll-contain",
124
+ children
125
+ })
126
+ })
127
+ })
128
+ }) });
129
+ }
130
+ const itemBase = [
131
+ "relative flex cursor-default items-center gap-2 rounded-item px-2 py-1.5",
132
+ "text-callout text-foreground-secondary outline-none select-none",
133
+ "data-[highlighted]:bg-control data-[highlighted]:text-foreground",
134
+ "data-[selected]:bg-accent-background data-[selected]:text-foreground",
135
+ "data-[disabled]:pointer-events-none data-[disabled]:text-foreground-disabled",
136
+ "[&_svg]:size-4",
137
+ "transition-colors duration-(--cl-duration-fast) ease-cl-out"
138
+ ].join(" ");
139
+ /**
140
+ * The item radius has its own token so nested corners remain comfortable
141
+ * inside the medium-radius popup.
142
+ */
143
+ function SelectItem({ className, ...props }) {
144
+ return /* @__PURE__ */ jsx(Select.Item, {
145
+ "data-cl-slot": "select-item",
146
+ className: cn(itemBase, className),
147
+ ...props
148
+ });
149
+ }
150
+ /**
151
+ * The selected mark, on the **trailing** edge of its item.
152
+ *
153
+ * The row is `[leading] [label] [gap] [check]`, which is the design's own — the
154
+ * check belongs after the value, where the eye already is, and it is what keeps
155
+ * the row honest in two ways that are easy to lose by reordering the JSX:
156
+ *
157
+ * - every row's label starts in the same column, because an unselected row has no
158
+ * indicator at all and a leading one would indent only the selected row;
159
+ * - Base UI aligns the popup so the selected item's *text* sits over the
160
+ * trigger's value text (native `<select>`, and the design's own overlap). A
161
+ * leading indicator makes that text column 24px wider than the trigger's, so the
162
+ * whole popup lands that far to the left of its anchor.
163
+ *
164
+ * `ml-auto` states the position in the indicator rather than relying on a sibling
165
+ * growing, so a row without a label still keeps its mark on the trailing edge.
166
+ */
167
+ function SelectItemIndicator({ className, ...props }) {
168
+ return /* @__PURE__ */ jsx(Select.ItemIndicator, {
169
+ "data-cl-slot": "select-item-indicator",
170
+ className: cn("ml-auto flex w-4 shrink-0 items-center justify-center text-accent", className),
171
+ ...props,
172
+ children: /* @__PURE__ */ jsx(CheckIcon, {})
173
+ });
174
+ }
175
+ /**
176
+ * The label, which takes the row's free space so the trailing indicator is pushed
177
+ * against the far edge — the `Expanded` of the Flutter row, and what lets a long
178
+ * label truncate instead of moving the check out of the popup.
179
+ */
180
+ function SelectItemText({ className, ...props }) {
181
+ return /* @__PURE__ */ jsx(Select.ItemText, {
182
+ className: cn("grow truncate", className),
183
+ ...props
184
+ });
185
+ }
186
+ function SelectGroup({ className, ...props }) {
187
+ return /* @__PURE__ */ jsx(Select.Group, {
188
+ className,
189
+ ...props
190
+ });
191
+ }
192
+ function SelectGroupLabel({ className, ...props }) {
193
+ return /* @__PURE__ */ jsx(Select.GroupLabel, {
194
+ className: cn("px-2 py-1.5 text-label text-foreground-hint", className),
195
+ ...props
196
+ });
197
+ }
198
+ function SelectSeparator({ className, ...props }) {
199
+ return /* @__PURE__ */ jsx(Select.Separator, {
200
+ className: cn("my-1 h-px bg-separator", className),
201
+ ...props
202
+ });
203
+ }
204
+ function SelectLabel({ className, ...props }) {
205
+ return /* @__PURE__ */ jsx(Select.Label, {
206
+ className: cn("text-label text-foreground-tertiary", className),
207
+ ...props
208
+ });
209
+ }
210
+ //#endregion
211
+ export { Select$1 as Select, SelectContent, SelectGroup, SelectGroupLabel, SelectIcon, SelectItem, SelectItemIndicator, SelectItemText, SelectLabel, SelectList, SelectPopup, SelectPortal, SelectPositioner, SelectSeparator, SelectTrigger, SelectValue };
212
+
213
+ //# sourceMappingURL=select.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"select.js","names":["Select","BaseSelect"],"sources":["../../src/ui/select.tsx"],"sourcesContent":["\"use client\";\n\nimport { Select as BaseSelect } from \"@base-ui/react/select\";\nimport { type ComponentProps, useRef } from \"react\";\nimport { Squircle } from \"@/lib/squircle\";\nimport { cn } from \"@/lib/utils\";\n\n/**\n * Inlined rather than pulled from an icon library: three glyphs is not worth a\n * dependency in a component consumers copy into their own project, and swapping\n * `lucide-react` in later is a one-line change per icon via `components.json`.\n */\nfunction ChevronDownIcon({ className, ...props }: ComponentProps<\"svg\">) {\n return (\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.6}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n className={className}\n {...props}\n >\n <path d=\"m4 6.5 4 4 4-4\" />\n </svg>\n );\n}\n\nfunction CheckIcon({ className, ...props }: ComponentProps<\"svg\">) {\n return (\n <svg\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.8}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n className={className}\n {...props}\n >\n <path d=\"m3.5 8.5 3 3 6-7\" />\n </svg>\n );\n}\n\nexport const Select: typeof BaseSelect.Root = BaseSelect.Root;\n\n/** Re-exported unstyled, for building something other than the default content. */\nexport const SelectPortal: typeof BaseSelect.Portal = BaseSelect.Portal;\nexport const SelectPositioner: typeof BaseSelect.Positioner = BaseSelect.Positioner;\nexport const SelectPopup: typeof BaseSelect.Popup = BaseSelect.Popup;\nexport const SelectList: typeof BaseSelect.List = BaseSelect.List;\n\nexport type SelectTriggerProps = Omit<ComponentProps<typeof BaseSelect.Trigger>, \"className\"> & {\n className?: string;\n /** Applied to the wrapper, which is what a caller's layout sizes. */\n wrapperClassName?: string;\n /**\n * Shared control-height tokens. Defaults to `md` rather than\n * the button's `lg`, because a select is usually one control among several in\n * a dense row.\n */\n size?: \"sm\" | \"md\" | \"lg\";\n};\n\nconst triggerSizes = {\n sm: \"h-control-sm px-2 text-caption\",\n md: \"h-control-md px-3 text-callout\",\n lg: \"h-control-lg px-4 text-body\",\n} as const;\n\nconst pressScales = {\n sm: \"[--cl-press-scale:var(--cl-press-scale-sm)]\",\n md: \"[--cl-press-scale:var(--cl-press-scale-md)]\",\n lg: \"[--cl-press-scale:var(--cl-press-scale-lg)]\",\n} as const;\n\nexport function SelectTrigger({\n className,\n wrapperClassName,\n size = \"md\",\n ...props\n}: SelectTriggerProps) {\n return (\n <Squircle\n asChild\n radius=\"control\"\n ring=\"field\"\n wrapperClassName={cn(\"cl-press w-full\", pressScales[size], wrapperClassName)}\n >\n <BaseSelect.Trigger\n data-cl-slot=\"select-trigger\"\n className={cn(\n \"group flex w-full items-center justify-between gap-2\",\n \"border border-outline bg-control text-left font-sans text-foreground\",\n \"hover:bg-control-highlight\",\n \"data-[popup-open]:bg-control-highlight\",\n \"data-[disabled]:pointer-events-none data-[disabled]:text-foreground-disabled\",\n \"data-[placeholder]:text-foreground-hint\",\n triggerSizes[size],\n className,\n )}\n {...props}\n />\n </Squircle>\n );\n}\n\nexport type SelectValueProps = Omit<ComponentProps<typeof BaseSelect.Value>, \"className\"> & {\n className?: string;\n};\n\nexport function SelectValue({ className, ...props }: SelectValueProps) {\n return (\n <BaseSelect.Value\n data-cl-slot=\"select-value\"\n className={cn(\"truncate\", className)}\n {...props}\n />\n );\n}\n\nexport type SelectIconProps = Omit<ComponentProps<typeof BaseSelect.Icon>, \"className\"> & {\n className?: string;\n};\n\n/**\n * Flips with the popup and is `aria-hidden` because Base UI already exposes the\n * expanded state; the glyph is decoration, not information.\n */\nexport function SelectIcon({ className, ...props }: SelectIconProps) {\n return (\n <BaseSelect.Icon\n data-cl-slot=\"select-icon\"\n className={cn(\n \"shrink-0 text-foreground-tertiary\",\n \"transition-transform duration-(--cl-duration-fast) ease-cl-out\",\n \"group-data-[popup-open]:rotate-180\",\n \"[&_svg]:size-4\",\n className,\n )}\n {...props}\n >\n <ChevronDownIcon />\n </BaseSelect.Icon>\n );\n}\n\nexport type SelectContentProps = Omit<ComponentProps<typeof BaseSelect.Popup>, \"className\"> & {\n /** Applied to the clipped element: the frost, padding and text. */\n className?: string;\n /** Applied to the wrapper, which is what is positioned and animated. */\n wrapperClassName?: string;\n /** Gap between the trigger and the popup, in px. */\n sideOffset?: number;\n /** Preferred side; Base UI flips when there is not enough room. */\n side?: ComponentProps<typeof BaseSelect.Positioner>[\"side\"];\n align?: ComponentProps<typeof BaseSelect.Positioner>[\"align\"];\n};\n\n/**\n * Portal, positioner and popup in the composition everyone actually wants.\n *\n * Two details come straight from Base UI:\n *\n * - `origin-[var(--transform-origin)]` — the positioner computes where the\n * popup should *appear to grow from* based on the trigger's position, so the\n * scale-in reads as the trigger unfolding rather than a box fading in.\n * - `--available-height` / `--anchor-width` — measured space, so the list never\n * spills past the viewport and the popup can match the trigger's width.\n */\nexport function SelectContent({\n className,\n wrapperClassName,\n children,\n sideOffset,\n side,\n align = \"start\",\n ...props\n}: SelectContentProps) {\n const positionerRef = useRef<HTMLDivElement>(null);\n const themedSideOffset = () => {\n const element = positionerRef.current;\n if (!element) return 0;\n const value = element.ownerDocument.defaultView\n ?.getComputedStyle(element)\n .getPropertyValue(\"--cl-select-side-offset\");\n return Number.parseFloat(value ?? \"\") || 0;\n };\n\n return (\n <BaseSelect.Portal>\n <BaseSelect.Positioner\n ref={positionerRef}\n sideOffset={sideOffset ?? themedSideOffset}\n side={side}\n align={align}\n className=\"z-50 outline-none select-none\"\n >\n {/*\n The width constraints and the entrance sit on the wrapper: it is what\n the SVG overlays anchor to, so it has to be exactly the shape's box,\n and it is what `.cl-enter-root` scales. `--transform-origin` is set by\n Base UI on the positioner above and inherits down, so the popup still\n grows out of the trigger.\n */}\n <Squircle\n asChild\n radius=\"medium\"\n wrapperClassName={cn(\n \"cl-enter-root origin-[var(--transform-origin)] w-fit\",\n \"min-w-[var(--anchor-width)] max-w-[min(var(--cl-select-max-width),var(--available-width))]\",\n wrapperClassName,\n )}\n >\n <BaseSelect.Popup\n data-cl-slot=\"select-content\"\n className={cn(\"cl-exit-sentinel cl-frost p-1 font-sans text-foreground\", className)}\n {...props}\n >\n <BaseSelect.List className=\"max-h-[min(var(--available-height),var(--cl-select-max-height))] overflow-y-auto overscroll-contain\">\n {children}\n </BaseSelect.List>\n </BaseSelect.Popup>\n </Squircle>\n </BaseSelect.Positioner>\n </BaseSelect.Portal>\n );\n}\n\nexport type SelectItemProps = Omit<ComponentProps<typeof BaseSelect.Item>, \"className\"> & {\n className?: string;\n};\n\nconst itemBase = [\n \"relative flex cursor-default items-center gap-2 rounded-item px-2 py-1.5\",\n \"text-callout text-foreground-secondary outline-none select-none\",\n \"data-[highlighted]:bg-control data-[highlighted]:text-foreground\",\n \"data-[selected]:bg-accent-background data-[selected]:text-foreground\",\n \"data-[disabled]:pointer-events-none data-[disabled]:text-foreground-disabled\",\n \"[&_svg]:size-4\",\n \"transition-colors duration-(--cl-duration-fast) ease-cl-out\",\n].join(\" \");\n\n/**\n * The item radius has its own token so nested corners remain comfortable\n * inside the medium-radius popup.\n */\nexport function SelectItem({ className, ...props }: SelectItemProps) {\n return (\n <BaseSelect.Item data-cl-slot=\"select-item\" className={cn(itemBase, className)} {...props} />\n );\n}\n\nexport type SelectItemIndicatorProps = Omit<\n ComponentProps<typeof BaseSelect.ItemIndicator>,\n \"className\"\n> & { className?: string };\n\n/**\n * The selected mark, on the **trailing** edge of its item.\n *\n * The row is `[leading] [label] [gap] [check]`, which is the design's own — the\n * check belongs after the value, where the eye already is, and it is what keeps\n * the row honest in two ways that are easy to lose by reordering the JSX:\n *\n * - every row's label starts in the same column, because an unselected row has no\n * indicator at all and a leading one would indent only the selected row;\n * - Base UI aligns the popup so the selected item's *text* sits over the\n * trigger's value text (native `<select>`, and the design's own overlap). A\n * leading indicator makes that text column 24px wider than the trigger's, so the\n * whole popup lands that far to the left of its anchor.\n *\n * `ml-auto` states the position in the indicator rather than relying on a sibling\n * growing, so a row without a label still keeps its mark on the trailing edge.\n */\nexport function SelectItemIndicator({ className, ...props }: SelectItemIndicatorProps) {\n return (\n <BaseSelect.ItemIndicator\n data-cl-slot=\"select-item-indicator\"\n className={cn(\"ml-auto flex w-4 shrink-0 items-center justify-center text-accent\", className)}\n {...props}\n >\n <CheckIcon />\n </BaseSelect.ItemIndicator>\n );\n}\n\nexport type SelectItemTextProps = Omit<ComponentProps<typeof BaseSelect.ItemText>, \"className\"> & {\n className?: string;\n};\n\n/**\n * The label, which takes the row's free space so the trailing indicator is pushed\n * against the far edge — the `Expanded` of the Flutter row, and what lets a long\n * label truncate instead of moving the check out of the popup.\n */\nexport function SelectItemText({ className, ...props }: SelectItemTextProps) {\n return <BaseSelect.ItemText className={cn(\"grow truncate\", className)} {...props} />;\n}\n\nexport type SelectGroupProps = Omit<ComponentProps<typeof BaseSelect.Group>, \"className\"> & {\n className?: string;\n};\n\nexport function SelectGroup({ className, ...props }: SelectGroupProps) {\n return <BaseSelect.Group className={className} {...props} />;\n}\n\nexport type SelectGroupLabelProps = Omit<\n ComponentProps<typeof BaseSelect.GroupLabel>,\n \"className\"\n> & { className?: string };\n\nexport function SelectGroupLabel({ className, ...props }: SelectGroupLabelProps) {\n return (\n <BaseSelect.GroupLabel\n className={cn(\"px-2 py-1.5 text-label text-foreground-hint\", className)}\n {...props}\n />\n );\n}\n\nexport type SelectSeparatorProps = Omit<\n ComponentProps<typeof BaseSelect.Separator>,\n \"className\"\n> & { className?: string };\n\nexport function SelectSeparator({ className, ...props }: SelectSeparatorProps) {\n return <BaseSelect.Separator className={cn(\"my-1 h-px bg-separator\", className)} {...props} />;\n}\n\nexport type SelectLabelProps = Omit<ComponentProps<typeof BaseSelect.Label>, \"className\"> & {\n className?: string;\n};\n\nexport function SelectLabel({ className, ...props }: SelectLabelProps) {\n return (\n <BaseSelect.Label className={cn(\"text-label text-foreground-tertiary\", className)} {...props} />\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ import { AnchoredSide } from "../lib/anchored.js";
2
+ import { ComponentProps, ReactNode } from "react";
3
+ import { Tooltip } from "@base-ui/react/tooltip";
4
+ //#region src/ui/tooltip.d.ts
5
+ export type TooltipProps = ComponentProps<typeof Tooltip.Root>;
6
+ export type TooltipMotion = "default" | "morph";
7
+ /**
8
+ * A tooltip and its trigger.
9
+ *
10
+ * `disableHoverablePopup` defaults to on, which Base UI does not: a ClaraLight
11
+ * tooltip is inert, so there is no popup to hover and keeping one open while
12
+ * the pointer travels towards it only delays the close. Pass `false` to get
13
+ * Base UI's behaviour back — worth doing only alongside a `TooltipContent` that
14
+ * has been made interactive.
15
+ */
16
+ declare function Tooltip$1({ disableHoverablePopup, ...props }: TooltipProps): import("react").JSX.Element;
17
+ /** Re-exported unstyled, for building something other than the default content. */
18
+ export declare const TooltipPortal: typeof Tooltip.Portal;
19
+ export declare const TooltipPositioner: typeof Tooltip.Positioner;
20
+ export declare const TooltipPopup: typeof Tooltip.Popup;
21
+ export declare const TooltipArrow: typeof Tooltip.Arrow;
22
+ export declare const TooltipViewport: typeof Tooltip.Viewport;
23
+ export type TooltipProviderProps = ComponentProps<typeof Tooltip.Provider>;
24
+ /**
25
+ * Shares one dwell across a group of tooltips: once any of them has been shown,
26
+ * the next opens instantly until `timeout` has passed.
27
+ *
28
+ * Worth wiring up even for a single tooltip — scanning a toolbar otherwise
29
+ * means waiting out the dwell at every control.
30
+ *
31
+ * `timeout` reads `--cl-tooltip-grace` from the document, not from an element:
32
+ * the provider renders no DOM of its own, and a grace period is a property of
33
+ * the whole document's pointer, not of one subtree's theme.
34
+ */
35
+ export declare function TooltipProvider({ timeout, ...props }: TooltipProviderProps): import("react").JSX.Element;
36
+ export type TooltipTriggerProps = Omit<ComponentProps<typeof Tooltip.Trigger>, "className"> & {
37
+ className?: string;
38
+ };
39
+ /**
40
+ * Hover and keyboard focus reveal the tooltip; Base UI owns both, along with
41
+ * dismissing it on Escape.
42
+ *
43
+ * There is deliberately no touch behaviour. A tooltip that needs a long press
44
+ * to read is a tooltip nobody reads — on touch, put the information in the
45
+ * interface or use a `Popover`.
46
+ *
47
+ * The dwell comes from `--cl-tooltip-delay` on the trigger's own cascade, so a
48
+ * scoped override applies to the controls inside it.
49
+ */
50
+ export declare function TooltipTrigger({ className, delay, closeDelay, ...props }: TooltipTriggerProps): import("react").JSX.Element;
51
+ export type TooltipContentProps = Omit<ComponentProps<typeof Tooltip.Popup>, "className"> & {
52
+ /** Applied to the clipped element: the frost, padding and text. */
53
+ className?: string;
54
+ /** Applied to the wrapper, which is what is positioned and animated. */
55
+ wrapperClassName?: string;
56
+ /** Preferred side of the trigger. Base UI flips when there is not enough room. */
57
+ side?: AnchoredSide;
58
+ align?: ComponentProps<typeof Tooltip.Positioner>["align"];
59
+ /** Gap between the trigger and the surface, in px. Measured from the arrow's tip. */
60
+ sideOffset?: number;
61
+ /** Space kept clear of the viewport edge, in px. */
62
+ collisionPadding?: number;
63
+ /** Whether the surface points at its trigger. */
64
+ arrow?: boolean;
65
+ /** How content behaves when one popup moves between multiple triggers. */
66
+ motion?: TooltipMotion;
67
+ children?: ReactNode;
68
+ };
69
+ /**
70
+ * The same anchored surface as `Popover`, one step quieter: a medium radius, a
71
+ * plain outline rather than the strong one, and the label's own padding.
72
+ *
73
+ * A tooltip never takes the pointer — hovering it must not keep it open, and it
74
+ * must not sit between the pointer and what it describes — so the whole
75
+ * positioner is inert.
76
+ */
77
+ export declare function TooltipContent({ className, wrapperClassName, side, align, sideOffset, collisionPadding, arrow, motion, children, ...props }: TooltipContentProps): import("react").JSX.Element;
78
+ export type TooltipGroupProps = Omit<TooltipProps, "children"> & {
79
+ /** The triggers that share one popup. Pass each label through `payload`. */
80
+ children?: ReactNode;
81
+ /** Applied to the shared popup surface. */
82
+ className?: string;
83
+ /** Applied to the shared popup wrapper. */
84
+ wrapperClassName?: string;
85
+ /** Preferred side of the trigger. Base UI flips when there is not enough room. */
86
+ side?: AnchoredSide;
87
+ align?: ComponentProps<typeof Tooltip.Positioner>["align"];
88
+ /** Gap between the trigger and the surface, in px. */
89
+ sideOffset?: number;
90
+ /** Space kept clear of the viewport edge, in px. */
91
+ collisionPadding?: number;
92
+ /** Whether the surface points at its trigger. */
93
+ arrow?: boolean;
94
+ /** How content behaves when the popup moves between triggers. */
95
+ motion?: TooltipMotion;
96
+ };
97
+ /**
98
+ * Several triggers backed by one mounted tooltip popup.
99
+ *
100
+ * Base UI keeps the popup alive while the active trigger changes, which lets
101
+ * ClaraLight animate the position, surface size and content instead of closing
102
+ * one tooltip and opening another. The regular `Tooltip` remains the right
103
+ * choice when each trigger needs its own placement or independent lifecycle.
104
+ */
105
+ export declare function TooltipGroup({ children, disableHoverablePopup, motion, side, align, sideOffset, collisionPadding, arrow, className, wrapperClassName, ...rootProps }: TooltipGroupProps): import("react").JSX.Element;
106
+ //#endregion
107
+ export { Tooltip$1 as Tooltip };
108
+ //# sourceMappingURL=tooltip.d.ts.map
@@ -0,0 +1,142 @@
1
+ "use client";
2
+ import { cn } from "../lib/utils.js";
3
+ import { AnchoredSurface, themedNumber, useThemedNumber } from "../lib/anchored.js";
4
+ import { useRef } from "react";
5
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
6
+ import { Tooltip } from "@base-ui/react/tooltip";
7
+ //#region src/ui/tooltip.tsx
8
+ /**
9
+ * A tooltip and its trigger.
10
+ *
11
+ * `disableHoverablePopup` defaults to on, which Base UI does not: a ClaraLight
12
+ * tooltip is inert, so there is no popup to hover and keeping one open while
13
+ * the pointer travels towards it only delays the close. Pass `false` to get
14
+ * Base UI's behaviour back — worth doing only alongside a `TooltipContent` that
15
+ * has been made interactive.
16
+ */
17
+ function Tooltip$1({ disableHoverablePopup = true, ...props }) {
18
+ return /* @__PURE__ */ jsx(Tooltip.Root, {
19
+ disableHoverablePopup,
20
+ ...props
21
+ });
22
+ }
23
+ /** Re-exported unstyled, for building something other than the default content. */
24
+ const TooltipPortal = Tooltip.Portal;
25
+ const TooltipPositioner = Tooltip.Positioner;
26
+ const TooltipPopup = Tooltip.Popup;
27
+ const TooltipArrow = Tooltip.Arrow;
28
+ const TooltipViewport = Tooltip.Viewport;
29
+ /**
30
+ * Shares one dwell across a group of tooltips: once any of them has been shown,
31
+ * the next opens instantly until `timeout` has passed.
32
+ *
33
+ * Worth wiring up even for a single tooltip — scanning a toolbar otherwise
34
+ * means waiting out the dwell at every control.
35
+ *
36
+ * `timeout` reads `--cl-tooltip-grace` from the document, not from an element:
37
+ * the provider renders no DOM of its own, and a grace period is a property of
38
+ * the whole document's pointer, not of one subtree's theme.
39
+ */
40
+ function TooltipProvider({ timeout, ...props }) {
41
+ const root = useRef(typeof document === "undefined" ? null : document.documentElement);
42
+ const grace = useThemedNumber(root, "--cl-tooltip-grace");
43
+ return /* @__PURE__ */ jsx(Tooltip.Provider, {
44
+ timeout: timeout ?? grace,
45
+ ...props
46
+ });
47
+ }
48
+ /**
49
+ * Hover and keyboard focus reveal the tooltip; Base UI owns both, along with
50
+ * dismissing it on Escape.
51
+ *
52
+ * There is deliberately no touch behaviour. A tooltip that needs a long press
53
+ * to read is a tooltip nobody reads — on touch, put the information in the
54
+ * interface or use a `Popover`.
55
+ *
56
+ * The dwell comes from `--cl-tooltip-delay` on the trigger's own cascade, so a
57
+ * scoped override applies to the controls inside it.
58
+ */
59
+ function TooltipTrigger({ className, delay, closeDelay, ...props }) {
60
+ const ref = useRef(null);
61
+ const dwell = useThemedNumber(ref, "--cl-tooltip-delay");
62
+ const close = useThemedNumber(ref, "--cl-tooltip-close-delay");
63
+ return /* @__PURE__ */ jsx(Tooltip.Trigger, {
64
+ ref,
65
+ delay: delay ?? dwell,
66
+ closeDelay: closeDelay ?? close,
67
+ className,
68
+ ...props
69
+ });
70
+ }
71
+ /**
72
+ * The same anchored surface as `Popover`, one step quieter: a medium radius, a
73
+ * plain outline rather than the strong one, and the label's own padding.
74
+ *
75
+ * A tooltip never takes the pointer — hovering it must not keep it open, and it
76
+ * must not sit between the pointer and what it describes — so the whole
77
+ * positioner is inert.
78
+ */
79
+ function TooltipContent({ className, wrapperClassName, side = "top", align = "center", sideOffset, collisionPadding, arrow = true, motion = "default", children, ...props }) {
80
+ const positionerRef = useRef(null);
81
+ const margin = useThemedNumber(positionerRef, "--cl-anchor-margin");
82
+ return /* @__PURE__ */ jsx(Tooltip.Portal, { children: /* @__PURE__ */ jsx(Tooltip.Positioner, {
83
+ ref: positionerRef,
84
+ side,
85
+ align,
86
+ sideOffset: sideOffset ?? (() => themedNumber(positionerRef.current, "--cl-anchor-gap") ?? 0),
87
+ collisionPadding: collisionPadding ?? margin,
88
+ sticky: true,
89
+ "data-cl-anchor-track": motion === "morph" ? "" : void 0,
90
+ className: cn("pointer-events-none z-50 outline-none", motion === "morph" && "cl-tooltip-positioner"),
91
+ children: /* @__PURE__ */ jsx(AnchoredSurface, {
92
+ asChild: true,
93
+ radius: "medium",
94
+ side,
95
+ arrow,
96
+ tailMotion: motion === "morph" ? "fast" : "none",
97
+ wrapperClassName: cn("w-fit", wrapperClassName),
98
+ children: /* @__PURE__ */ jsxs(Tooltip.Popup, {
99
+ "data-cl-slot": "tooltip-content",
100
+ className: cn("cl-exit-sentinel cl-frost border-outline shadow-none", "font-sans text-callout text-foreground-secondary", "[--cl-anchored-padding-x:var(--cl-tooltip-padding-x)]", "[--cl-anchored-padding-y:var(--cl-tooltip-padding-y)]", motion === "morph" && "cl-tooltip-popup", className),
101
+ ...props,
102
+ children: [motion === "morph" ? /* @__PURE__ */ jsx(Tooltip.Viewport, {
103
+ "data-cl-slot": "tooltip-viewport",
104
+ className: "cl-tooltip-viewport",
105
+ children
106
+ }) : children, arrow ? /* @__PURE__ */ jsx(Tooltip.Arrow, {
107
+ "data-cl-anchor-probe": "",
108
+ className: "cl-anchor-probe"
109
+ }) : null]
110
+ })
111
+ })
112
+ }) });
113
+ }
114
+ /**
115
+ * Several triggers backed by one mounted tooltip popup.
116
+ *
117
+ * Base UI keeps the popup alive while the active trigger changes, which lets
118
+ * ClaraLight animate the position, surface size and content instead of closing
119
+ * one tooltip and opening another. The regular `Tooltip` remains the right
120
+ * choice when each trigger needs its own placement or independent lifecycle.
121
+ */
122
+ function TooltipGroup({ children, disableHoverablePopup = true, motion = "morph", side, align, sideOffset, collisionPadding, arrow, className, wrapperClassName, ...rootProps }) {
123
+ return /* @__PURE__ */ jsx(Tooltip.Root, {
124
+ disableHoverablePopup,
125
+ ...rootProps,
126
+ children: ({ payload }) => /* @__PURE__ */ jsxs(Fragment$1, { children: [children, /* @__PURE__ */ jsx(TooltipContent, {
127
+ motion,
128
+ side,
129
+ align,
130
+ sideOffset,
131
+ collisionPadding,
132
+ arrow,
133
+ className,
134
+ wrapperClassName,
135
+ children: payload
136
+ })] })
137
+ });
138
+ }
139
+ //#endregion
140
+ export { Tooltip$1 as Tooltip, TooltipArrow, TooltipContent, TooltipGroup, TooltipPopup, TooltipPortal, TooltipPositioner, TooltipProvider, TooltipTrigger, TooltipViewport };
141
+
142
+ //# sourceMappingURL=tooltip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tooltip.js","names":["Tooltip","BaseTooltip"],"sources":["../../src/ui/tooltip.tsx"],"sourcesContent":["\"use client\";\n\nimport { Tooltip as BaseTooltip } from \"@base-ui/react/tooltip\";\nimport { type ComponentProps, type ReactNode, useRef } from \"react\";\nimport { type AnchoredSide, AnchoredSurface, themedNumber, useThemedNumber } from \"@/lib/anchored\";\nimport { cn } from \"@/lib/utils\";\n\nexport type TooltipProps = ComponentProps<typeof BaseTooltip.Root>;\nexport type TooltipMotion = \"default\" | \"morph\";\n\n/**\n * A tooltip and its trigger.\n *\n * `disableHoverablePopup` defaults to on, which Base UI does not: a ClaraLight\n * tooltip is inert, so there is no popup to hover and keeping one open while\n * the pointer travels towards it only delays the close. Pass `false` to get\n * Base UI's behaviour back — worth doing only alongside a `TooltipContent` that\n * has been made interactive.\n */\nexport function Tooltip({ disableHoverablePopup = true, ...props }: TooltipProps) {\n return <BaseTooltip.Root disableHoverablePopup={disableHoverablePopup} {...props} />;\n}\n\n/** Re-exported unstyled, for building something other than the default content. */\nexport const TooltipPortal: typeof BaseTooltip.Portal = BaseTooltip.Portal;\nexport const TooltipPositioner: typeof BaseTooltip.Positioner = BaseTooltip.Positioner;\nexport const TooltipPopup: typeof BaseTooltip.Popup = BaseTooltip.Popup;\nexport const TooltipArrow: typeof BaseTooltip.Arrow = BaseTooltip.Arrow;\nexport const TooltipViewport: typeof BaseTooltip.Viewport = BaseTooltip.Viewport;\n\nexport type TooltipProviderProps = ComponentProps<typeof BaseTooltip.Provider>;\n\n/**\n * Shares one dwell across a group of tooltips: once any of them has been shown,\n * the next opens instantly until `timeout` has passed.\n *\n * Worth wiring up even for a single tooltip — scanning a toolbar otherwise\n * means waiting out the dwell at every control.\n *\n * `timeout` reads `--cl-tooltip-grace` from the document, not from an element:\n * the provider renders no DOM of its own, and a grace period is a property of\n * the whole document's pointer, not of one subtree's theme.\n */\nexport function TooltipProvider({ timeout, ...props }: TooltipProviderProps) {\n // Not a ref onto anything rendered here — the provider renders nothing. It\n // reuses the reader's shape to keep the read in a layout effect rather than\n // in render, where it would force a synchronous layout on every pass.\n const root = useRef<Element | null>(\n typeof document === \"undefined\" ? null : document.documentElement,\n );\n const grace = useThemedNumber(root, \"--cl-tooltip-grace\");\n return <BaseTooltip.Provider timeout={timeout ?? grace} {...props} />;\n}\n\nexport type TooltipTriggerProps = Omit<ComponentProps<typeof BaseTooltip.Trigger>, \"className\"> & {\n className?: string;\n};\n\n/**\n * Hover and keyboard focus reveal the tooltip; Base UI owns both, along with\n * dismissing it on Escape.\n *\n * There is deliberately no touch behaviour. A tooltip that needs a long press\n * to read is a tooltip nobody reads — on touch, put the information in the\n * interface or use a `Popover`.\n *\n * The dwell comes from `--cl-tooltip-delay` on the trigger's own cascade, so a\n * scoped override applies to the controls inside it.\n */\nexport function TooltipTrigger({ className, delay, closeDelay, ...props }: TooltipTriggerProps) {\n const ref = useRef<HTMLButtonElement>(null);\n const dwell = useThemedNumber(ref, \"--cl-tooltip-delay\");\n const close = useThemedNumber(ref, \"--cl-tooltip-close-delay\");\n return (\n <BaseTooltip.Trigger\n ref={ref}\n delay={delay ?? dwell}\n closeDelay={closeDelay ?? close}\n className={className}\n {...props}\n />\n );\n}\n\nexport type TooltipContentProps = Omit<ComponentProps<typeof BaseTooltip.Popup>, \"className\"> & {\n /** Applied to the clipped element: the frost, padding and text. */\n className?: string;\n /** Applied to the wrapper, which is what is positioned and animated. */\n wrapperClassName?: string;\n /** Preferred side of the trigger. Base UI flips when there is not enough room. */\n side?: AnchoredSide;\n align?: ComponentProps<typeof BaseTooltip.Positioner>[\"align\"];\n /** Gap between the trigger and the surface, in px. Measured from the arrow's tip. */\n sideOffset?: number;\n /** Space kept clear of the viewport edge, in px. */\n collisionPadding?: number;\n /** Whether the surface points at its trigger. */\n arrow?: boolean;\n /** How content behaves when one popup moves between multiple triggers. */\n motion?: TooltipMotion;\n children?: ReactNode;\n};\n\n/**\n * The same anchored surface as `Popover`, one step quieter: a medium radius, a\n * plain outline rather than the strong one, and the label's own padding.\n *\n * A tooltip never takes the pointer — hovering it must not keep it open, and it\n * must not sit between the pointer and what it describes — so the whole\n * positioner is inert.\n */\nexport function TooltipContent({\n className,\n wrapperClassName,\n side = \"top\",\n align = \"center\",\n sideOffset,\n collisionPadding,\n arrow = true,\n motion = \"default\",\n children,\n ...props\n}: TooltipContentProps) {\n const positionerRef = useRef<HTMLDivElement>(null);\n const margin = useThemedNumber(positionerRef, \"--cl-anchor-margin\");\n\n return (\n <BaseTooltip.Portal>\n <BaseTooltip.Positioner\n ref={positionerRef}\n side={side}\n align={align}\n sideOffset={\n sideOffset ?? (() => themedNumber(positionerRef.current, \"--cl-anchor-gap\") ?? 0)\n }\n collisionPadding={collisionPadding ?? margin}\n sticky\n /*\n * `data-cl-anchor-track` marks this as the element whose travel the tail\n * leads. Base UI writes the place it is going to as inline insets and the\n * transition below walks them, so the surface can read its own remaining\n * travel off it and keep the tail on the new trigger while the body is\n * still on its way; see `AnchoredSurface`.\n */\n data-cl-anchor-track={motion === \"morph\" ? \"\" : undefined}\n className={cn(\n \"pointer-events-none z-50 outline-none\",\n motion === \"morph\" && \"cl-tooltip-positioner\",\n )}\n >\n <AnchoredSurface\n asChild\n radius=\"medium\"\n side={side}\n arrow={arrow}\n tailMotion={motion === \"morph\" ? \"fast\" : \"none\"}\n wrapperClassName={cn(\"w-fit\", wrapperClassName)}\n >\n <BaseTooltip.Popup\n data-cl-slot=\"tooltip-content\"\n className={cn(\n \"cl-exit-sentinel cl-frost border-outline shadow-none\",\n \"font-sans text-callout text-foreground-secondary\",\n \"[--cl-anchored-padding-x:var(--cl-tooltip-padding-x)]\",\n \"[--cl-anchored-padding-y:var(--cl-tooltip-padding-y)]\",\n motion === \"morph\" && \"cl-tooltip-popup\",\n className,\n )}\n {...props}\n >\n {motion === \"morph\" ? (\n <BaseTooltip.Viewport data-cl-slot=\"tooltip-viewport\" className=\"cl-tooltip-viewport\">\n {children}\n </BaseTooltip.Viewport>\n ) : (\n children\n )}\n {arrow ? (\n <BaseTooltip.Arrow data-cl-anchor-probe=\"\" className=\"cl-anchor-probe\" />\n ) : null}\n </BaseTooltip.Popup>\n </AnchoredSurface>\n </BaseTooltip.Positioner>\n </BaseTooltip.Portal>\n );\n}\n\nexport type TooltipGroupProps = Omit<TooltipProps, \"children\"> & {\n /** The triggers that share one popup. Pass each label through `payload`. */\n children?: ReactNode;\n /** Applied to the shared popup surface. */\n className?: string;\n /** Applied to the shared popup wrapper. */\n wrapperClassName?: string;\n /** Preferred side of the trigger. Base UI flips when there is not enough room. */\n side?: AnchoredSide;\n align?: ComponentProps<typeof BaseTooltip.Positioner>[\"align\"];\n /** Gap between the trigger and the surface, in px. */\n sideOffset?: number;\n /** Space kept clear of the viewport edge, in px. */\n collisionPadding?: number;\n /** Whether the surface points at its trigger. */\n arrow?: boolean;\n /** How content behaves when the popup moves between triggers. */\n motion?: TooltipMotion;\n};\n\n/**\n * Several triggers backed by one mounted tooltip popup.\n *\n * Base UI keeps the popup alive while the active trigger changes, which lets\n * ClaraLight animate the position, surface size and content instead of closing\n * one tooltip and opening another. The regular `Tooltip` remains the right\n * choice when each trigger needs its own placement or independent lifecycle.\n */\nexport function TooltipGroup({\n children,\n disableHoverablePopup = true,\n motion = \"morph\",\n side,\n align,\n sideOffset,\n collisionPadding,\n arrow,\n className,\n wrapperClassName,\n ...rootProps\n}: TooltipGroupProps) {\n return (\n <BaseTooltip.Root disableHoverablePopup={disableHoverablePopup} {...rootProps}>\n {({ payload }) => (\n <>\n {children}\n <TooltipContent\n motion={motion}\n side={side}\n align={align}\n sideOffset={sideOffset}\n collisionPadding={collisionPadding}\n arrow={arrow}\n className={className}\n wrapperClassName={wrapperClassName}\n >\n {payload as ReactNode}\n </TooltipContent>\n </>\n )}\n </BaseTooltip.Root>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAmBA,SAAgBA,UAAQ,EAAE,wBAAwB,MAAM,GAAG,SAAuB;CAChF,OAAO,oBAACC,QAAY,MAAb;EAAyC;EAAuB,GAAI;CAAQ,CAAA;AACrF;;AAGA,MAAa,gBAA2CA,QAAY;AACpE,MAAa,oBAAmDA,QAAY;AAC5E,MAAa,eAAyCA,QAAY;AAClE,MAAa,eAAyCA,QAAY;AAClE,MAAa,kBAA+CA,QAAY;;;;;;;;;;;;AAexE,SAAgB,gBAAgB,EAAE,SAAS,GAAG,SAA+B;CAI3E,MAAM,OAAO,OACX,OAAO,aAAa,cAAc,OAAO,SAAS,eACpD;CACA,MAAM,QAAQ,gBAAgB,MAAM,oBAAoB;CACxD,OAAO,oBAACA,QAAY,UAAb;EAAsB,SAAS,WAAW;EAAO,GAAI;CAAQ,CAAA;AACtE;;;;;;;;;;;;AAiBA,SAAgB,eAAe,EAAE,WAAW,OAAO,YAAY,GAAG,SAA8B;CAC9F,MAAM,MAAM,OAA0B,IAAI;CAC1C,MAAM,QAAQ,gBAAgB,KAAK,oBAAoB;CACvD,MAAM,QAAQ,gBAAgB,KAAK,0BAA0B;CAC7D,OACE,oBAACA,QAAY,SAAb;EACO;EACL,OAAO,SAAS;EAChB,YAAY,cAAc;EACf;EACX,GAAI;CACL,CAAA;AAEL;;;;;;;;;AA6BA,SAAgB,eAAe,EAC7B,WACA,kBACA,OAAO,OACP,QAAQ,UACR,YACA,kBACA,QAAQ,MACR,SAAS,WACT,UACA,GAAG,SACmB;CACtB,MAAM,gBAAgB,OAAuB,IAAI;CACjD,MAAM,SAAS,gBAAgB,eAAe,oBAAoB;CAElE,OACE,oBAACA,QAAY,QAAb,EAAA,UACE,oBAACA,QAAY,YAAb;EACE,KAAK;EACC;EACC;EACP,YACE,qBAAqB,aAAa,cAAc,SAAS,iBAAiB,KAAK;EAEjF,kBAAkB,oBAAoB;EACtC,QAAA;EAQA,wBAAsB,WAAW,UAAU,KAAK,KAAA;EAChD,WAAW,GACT,yCACA,WAAW,WAAW,uBACxB;EAEA,UAAA,oBAAC,iBAAD;GACE,SAAA;GACA,QAAO;GACD;GACC;GACP,YAAY,WAAW,UAAU,SAAS;GAC1C,kBAAkB,GAAG,SAAS,gBAAgB;GAE9C,UAAA,qBAACA,QAAY,OAAb;IACE,gBAAa;IACb,WAAW,GACT,wDACA,oDACA,yDACA,yDACA,WAAW,WAAW,oBACtB,SACF;IACA,GAAI;IAVN,UAAA,CAYG,WAAW,UACV,oBAACA,QAAY,UAAb;KAAsB,gBAAa;KAAmB,WAAU;KAC7D;IACmB,CAAA,IAEtB,UAED,QACC,oBAACA,QAAY,OAAb;KAAmB,wBAAqB;KAAG,WAAU;IAAmB,CAAA,IACtE,IACa;;EACJ,CAAA;CACK,CAAA,EACN,CAAA;AAExB;;;;;;;;;AA8BA,SAAgB,aAAa,EAC3B,UACA,wBAAwB,MACxB,SAAS,SACT,MACA,OACA,YACA,kBACA,OACA,WACA,kBACA,GAAG,aACiB;CACpB,OACE,oBAACA,QAAY,MAAb;EAAyC;EAAuB,GAAI;EAChE,WAAA,EAAE,cACF,qBAAA,YAAA,EAAA,UAAA,CACG,UACD,oBAAC,gBAAD;GACU;GACF;GACC;GACK;GACM;GACX;GACI;GACO;GAEjB,UAAA;EACa,CAAA,CAChB,EAAA,CAAA;CAEY,CAAA;AAEtB"}
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "@claralight-design/react",
3
+ "version": "0.0.1",
4
+ "description": "ClaraLight Design \u2014 a quiet, layered design language for React. Base UI primitives, Tailwind v4 tokens, copy-in source.",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "sideEffects": [
8
+ "**/*.css"
9
+ ],
10
+ "files": [
11
+ "dist",
12
+ "styles"
13
+ ],
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "import": "./dist/index.js"
18
+ },
19
+ "./styles.css": "./styles/index.css",
20
+ "./theme.css": "./styles/theme.css",
21
+ "./base.css": "./styles/base.css",
22
+ "./anchored.css": "./styles/anchored.css",
23
+ "./tooltip.css": "./styles/tooltip.css",
24
+ "./scroll-area.css": "./styles/scroll-area.css",
25
+ "./package.json": "./package.json"
26
+ },
27
+ "scripts": {
28
+ "build": "tsdown",
29
+ "dev": "tsdown --watch",
30
+ "prepack": "pnpm build && pnpm typecheck",
31
+ "typecheck": "tsc --noEmit",
32
+ "test": "vitest run",
33
+ "test:watch": "vitest",
34
+ "check:exports": "publint --strict"
35
+ },
36
+ "dependencies": {
37
+ "@lisse/core": "catalog:",
38
+ "@lisse/react": "catalog:",
39
+ "class-variance-authority": "^0.7.1",
40
+ "cn": "^0.2.6"
41
+ },
42
+ "peerDependencies": {
43
+ "@base-ui/react": "^1.8.0",
44
+ "react": "^19.0.0",
45
+ "react-dom": "^19.0.0"
46
+ },
47
+ "devDependencies": {
48
+ "@base-ui/react": "catalog:",
49
+ "@biomejs/biome": "catalog:",
50
+ "@types/node": "catalog:",
51
+ "@types/react": "catalog:",
52
+ "@types/react-dom": "catalog:",
53
+ "class-variance-authority": "catalog:",
54
+ "publint": "catalog:",
55
+ "react": "catalog:",
56
+ "react-dom": "catalog:",
57
+ "tsdown": "catalog:",
58
+ "typescript": "catalog:",
59
+ "vitest": "catalog:"
60
+ },
61
+ "keywords": [
62
+ "claralight",
63
+ "design-system",
64
+ "react",
65
+ "base-ui",
66
+ "tailwindcss",
67
+ "registry"
68
+ ],
69
+ "publishConfig": {
70
+ "access": "public"
71
+ }
72
+ }