@xingxingmofashu/ui 0.1.0 → 0.1.2

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 (38) hide show
  1. package/README.md +124 -72
  2. package/dist/components/accordion.mjs +4 -9
  3. package/dist/components/checkbox.d.ts +1 -1
  4. package/dist/components/checkbox.mjs +27 -19
  5. package/dist/components/icons/check.d.ts +3 -0
  6. package/dist/components/icons/check.mjs +24 -0
  7. package/dist/components/icons/close.d.ts +3 -0
  8. package/dist/components/icons/close.mjs +21 -0
  9. package/dist/components/icons/dash.d.ts +3 -0
  10. package/dist/components/icons/dash.mjs +24 -0
  11. package/dist/components/icons/error.d.ts +3 -0
  12. package/dist/components/icons/error.mjs +23 -0
  13. package/dist/components/icons/info.d.ts +3 -0
  14. package/dist/components/icons/info.mjs +23 -0
  15. package/dist/components/icons/loading.d.ts +3 -0
  16. package/dist/components/icons/loading.mjs +22 -0
  17. package/dist/components/icons/plus.d.ts +3 -0
  18. package/dist/components/icons/plus.mjs +22 -0
  19. package/dist/components/icons/success.d.ts +3 -0
  20. package/dist/components/icons/success.mjs +22 -0
  21. package/dist/components/icons/warning.d.ts +3 -0
  22. package/dist/components/icons/warning.mjs +23 -0
  23. package/dist/components/input.d.ts +1 -1
  24. package/dist/components/input.mjs +17 -1
  25. package/dist/components/slider.d.ts +9 -3
  26. package/dist/components/slider.mjs +33 -0
  27. package/dist/components/switch.d.ts +1 -1
  28. package/dist/components/switch.mjs +24 -1
  29. package/dist/components/toast.d.ts +12 -11
  30. package/dist/components/toast.mjs +58 -87
  31. package/dist/index.css +1 -1
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.mjs +1 -0
  34. package/dist/internal/aria.d.ts +3 -0
  35. package/dist/internal/aria.mjs +5 -0
  36. package/dist/theme/toast.d.ts +15 -36
  37. package/dist/theme/toast.mjs +28 -20
  38. package/package.json +2 -2
@@ -2,6 +2,7 @@
2
2
  import { Input as UIInput } from "@base-ui/react/input";
3
3
  import { cn } from "cn";
4
4
  import * as React from "react";
5
+ import { mergeIds } from "../internal/aria.mjs";
5
6
  import theme from "../theme/input.mjs";
6
7
  function Input({
7
8
  className,
@@ -12,11 +13,17 @@ function Input({
12
13
  size,
13
14
  id,
14
15
  disabled,
16
+ "aria-describedby": ariaDescribedBy,
15
17
  ...props
16
18
  }) {
17
19
  const ui = theme({ size });
18
20
  const generatedId = React.useId();
21
+ const descriptionId = React.useId();
19
22
  const inputId = id ?? generatedId;
23
+ const describedBy = mergeIds(
24
+ ariaDescribedBy,
25
+ description ? descriptionId : void 0
26
+ );
20
27
  return /* @__PURE__ */ React.createElement(
21
28
  "div",
22
29
  {
@@ -46,6 +53,7 @@ function Input({
46
53
  ...props,
47
54
  id: inputId,
48
55
  disabled,
56
+ "aria-describedby": describedBy,
49
57
  "data-slot": "input",
50
58
  className: ui.input()
51
59
  }
@@ -58,7 +66,15 @@ function Input({
58
66
  },
59
67
  trailingIcon
60
68
  ) : null),
61
- description ? /* @__PURE__ */ React.createElement("span", { "data-slot": "description", className: ui.description() }, description) : null
69
+ description ? /* @__PURE__ */ React.createElement(
70
+ "span",
71
+ {
72
+ id: descriptionId,
73
+ "data-slot": "description",
74
+ className: ui.description()
75
+ },
76
+ description
77
+ ) : null
62
78
  );
63
79
  }
64
80
  export { Input };
@@ -1,12 +1,18 @@
1
1
  import { Slider as UISlider } from "@base-ui/react/slider";
2
+ import * as React from "react";
2
3
  type SliderProps<Value extends number | readonly number[] = number> = Omit<UISlider.Root.Props<Value>, "children" | "style" | "className"> & {
3
4
  /** Tailwind classes merged into the root. */
4
5
  className?: any;
6
+ /** Accessible name for the thumb at `index`. Useful for range sliders. */
7
+ getAriaLabel?: UISlider.Thumb.Props["getAriaLabel"];
8
+ /** Accessible value text for the thumb at `index`. */
9
+ getAriaValueText?: UISlider.Thumb.Props["getAriaValueText"];
5
10
  };
6
11
  /**
7
- * Sliders need an accessible name. Pass `aria-labelledby`; Base UI forwards it
8
- * from the root to every thumb.
12
+ * Sliders need an accessible name. Pass `aria-label` or `aria-labelledby`;
13
+ * both are forwarded from the root to every thumb. `aria-describedby` is also
14
+ * forwarded so supporting copy is announced with each thumb.
9
15
  */
10
- declare function Slider<Value extends number | readonly number[] = number>({ className, value, defaultValue, ...props }: SliderProps<Value>): import("react").JSX.Element;
16
+ declare function Slider<Value extends number | readonly number[] = number>({ className, value, defaultValue, "aria-describedby": ariaDescribedBy, "aria-errormessage": ariaErrorMessage, "aria-invalid": ariaInvalid, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, getAriaLabel, getAriaValueText, ...props }: SliderProps<Value>): React.JSX.Element;
11
17
  export { Slider };
12
18
  export type { SliderProps };
@@ -1,20 +1,47 @@
1
1
  "use client";
2
2
  import { Slider as UISlider } from "@base-ui/react/slider";
3
3
  import { cn } from "cn";
4
+ import * as React from "react";
4
5
  import theme from "../theme/slider.mjs";
6
+ function setAriaAttribute(input, name, value) {
7
+ if (value === void 0 || value === null) {
8
+ input.removeAttribute(name);
9
+ return;
10
+ }
11
+ input.setAttribute(name, String(value));
12
+ }
5
13
  function Slider({
6
14
  className,
7
15
  value,
8
16
  defaultValue,
17
+ "aria-describedby": ariaDescribedBy,
18
+ "aria-errormessage": ariaErrorMessage,
19
+ "aria-invalid": ariaInvalid,
20
+ "aria-label": ariaLabel,
21
+ "aria-labelledby": ariaLabelledBy,
22
+ getAriaLabel,
23
+ getAriaValueText,
9
24
  ...props
10
25
  }) {
11
26
  const ui = theme();
27
+ const syncThumbAria = React.useCallback(
28
+ (input) => {
29
+ if (!input) {
30
+ return;
31
+ }
32
+ setAriaAttribute(input, "aria-invalid", ariaInvalid);
33
+ setAriaAttribute(input, "aria-errormessage", ariaErrorMessage);
34
+ },
35
+ [ariaErrorMessage, ariaInvalid]
36
+ );
12
37
  const values = value ?? defaultValue;
13
38
  const thumbCount = Array.isArray(values) ? Math.max(1, values.length) : 1;
14
39
  return /* @__PURE__ */ React.createElement(
15
40
  UISlider.Root,
16
41
  {
17
42
  ...props,
43
+ "aria-label": ariaLabel,
44
+ "aria-labelledby": ariaLabelledBy,
18
45
  value,
19
46
  defaultValue,
20
47
  "data-slot": "root",
@@ -31,6 +58,12 @@ function Slider({
31
58
  {
32
59
  key: index,
33
60
  index,
61
+ "aria-describedby": ariaDescribedBy,
62
+ "aria-label": ariaLabel,
63
+ "aria-labelledby": ariaLabelledBy,
64
+ getAriaLabel,
65
+ getAriaValueText,
66
+ inputRef: syncThumbAria,
34
67
  "data-slot": "thumb",
35
68
  className: ui.thumb()
36
69
  },
@@ -12,6 +12,6 @@ interface SwitchProps extends Omit<UISwitch.Root.Props, "className" | "children"
12
12
  /** Optional supporting text. */
13
13
  description?: React.ReactNode;
14
14
  }
15
- declare function Switch({ className, label, description, size, disabled, readOnly, onCheckedChange, ...props }: SwitchProps): React.JSX.Element;
15
+ declare function Switch({ className, label, description, size, disabled, readOnly, onCheckedChange, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...props }: SwitchProps): React.JSX.Element;
16
16
  export { Switch };
17
17
  export type { SwitchProps };
@@ -2,6 +2,7 @@
2
2
  import { Switch as UISwitch } from "@base-ui/react/switch";
3
3
  import { cn } from "cn";
4
4
  import * as React from "react";
5
+ import { mergeIds } from "../internal/aria.mjs";
5
6
  import theme from "../theme/switch.mjs";
6
7
  function LiquidGlassFilter({ id }) {
7
8
  return /* @__PURE__ */ React.createElement("svg", { "aria-hidden": "true", focusable: "false", "data-layer": "filter" }, /* @__PURE__ */ React.createElement("defs", null, /* @__PURE__ */ React.createElement("filter", { id: `${id}-goo` }, /* @__PURE__ */ React.createElement(
@@ -53,10 +54,21 @@ function Switch({
53
54
  disabled,
54
55
  readOnly,
55
56
  onCheckedChange,
57
+ "aria-describedby": ariaDescribedBy,
58
+ "aria-label": ariaLabel,
59
+ "aria-labelledby": ariaLabelledBy,
56
60
  ...props
57
61
  }) {
58
62
  const ui = theme({ size });
59
63
  const filterId = React.useId();
64
+ const labelId = React.useId();
65
+ const descriptionId = React.useId();
66
+ const hasName = Boolean(label || ariaLabel || ariaLabelledBy);
67
+ const labelledBy = ariaLabelledBy || (label && !ariaLabel ? labelId : void 0);
68
+ const describedBy = mergeIds(
69
+ ariaDescribedBy,
70
+ description && hasName ? descriptionId : void 0
71
+ );
60
72
  const filterStyles = {
61
73
  "--ui-switch-goo-filter": `url("#${filterId}-goo")`,
62
74
  "--ui-switch-remove-black-filter": `url("#${filterId}-remove-black")`
@@ -80,6 +92,9 @@ function Switch({
80
92
  UISwitch.Root,
81
93
  {
82
94
  ...props,
95
+ "aria-describedby": describedBy,
96
+ "aria-label": ariaLabel,
97
+ "aria-labelledby": labelledBy,
83
98
  disabled,
84
99
  readOnly,
85
100
  "data-slot": "root",
@@ -109,7 +124,15 @@ function Switch({
109
124
  /* @__PURE__ */ React.createElement("span", { "data-layer": "cover" })
110
125
  )
111
126
  )),
112
- label || description ? /* @__PURE__ */ React.createElement("span", { "data-slot": "wrapper", className: ui.wrapper() }, label ? /* @__PURE__ */ React.createElement("span", { "data-slot": "label", className: ui.label() }, label) : null, description ? /* @__PURE__ */ React.createElement("span", { "data-slot": "description", className: ui.description() }, description) : null) : null
127
+ label || description ? /* @__PURE__ */ React.createElement("span", { "data-slot": "wrapper", className: ui.wrapper() }, label ? /* @__PURE__ */ React.createElement("span", { id: labelId, "data-slot": "label", className: ui.label() }, label) : null, description ? /* @__PURE__ */ React.createElement(
128
+ "span",
129
+ {
130
+ id: descriptionId,
131
+ "data-slot": "description",
132
+ className: ui.description()
133
+ },
134
+ description
135
+ ) : null) : null
113
136
  );
114
137
  }
115
138
  export { Switch };
@@ -1,19 +1,20 @@
1
1
  import { Toast as UIToast } from "@base-ui/react/toast";
2
2
  import * as React from "react";
3
- /** Corner or edge where the toast stack is anchored. */
4
- type ToastPosition = "top-left" | "top-center" | "top-right" | "bottom-left" | "bottom-center" | "bottom-right";
3
+ /** Corner where the toast stack is anchored. */
4
+ type ToastPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right";
5
5
  interface ToastProviderProps extends Omit<UIToast.Provider.Props, "children"> {
6
6
  /** App content that can call `useToast`. */
7
7
  children?: React.ReactNode;
8
- /** Corner or edge where the toast stack is anchored. */
8
+ }
9
+ interface ToastProps extends Omit<UIToast.Root.Props, "children" | "className" | "swipeDirection" | "toast"> {
10
+ /** Corner where the toast stack is anchored. */
9
11
  position?: ToastPosition;
10
- /** Accessible label for the close button. */
11
- closeLabel?: string;
12
- /** Accessible label for the notification region. */
13
- viewportLabel?: string;
14
- /** Tailwind classes merged into the viewport. */
12
+ /** Content rendered in place of the default close button. */
13
+ close?: React.ReactNode;
14
+ /** Tailwind classes merged into each toast. */
15
15
  className?: string;
16
16
  }
17
- declare function ToastProvider({ children, position, closeLabel, viewportLabel, className, ...props }: ToastProviderProps): React.JSX.Element;
18
- export { ToastProvider };
19
- export type { ToastPosition, ToastProviderProps };
17
+ declare function ToastProvider({ children, ...props }: ToastProviderProps): React.JSX.Element;
18
+ declare function Toast({ position, close, className, ...props }: ToastProps): React.JSX.Element;
19
+ export { Toast, ToastProvider };
20
+ export type { ToastPosition, ToastProps, ToastProviderProps };
@@ -4,52 +4,31 @@ import { cn } from "cn";
4
4
  import * as React from "react";
5
5
  import { useToast } from "../composables/use-toast.mjs";
6
6
  import theme from "../theme/toast.mjs";
7
- function CloseIcon() {
8
- return /* @__PURE__ */ React.createElement(
9
- "svg",
10
- {
11
- "aria-hidden": "true",
12
- viewBox: "0 0 16 16",
13
- fill: "none",
14
- stroke: "currentColor",
15
- strokeWidth: "1.5",
16
- strokeLinecap: "round",
17
- className: "size-4"
18
- },
19
- /* @__PURE__ */ React.createElement("path", { d: "m4 4 8 8M12 4l-8 8" })
20
- );
21
- }
22
- function statusIconContent(type) {
23
- const svgProps = {
24
- viewBox: "0 0 20 20",
25
- fill: "none",
26
- stroke: "currentColor",
27
- strokeWidth: 1.5,
28
- strokeLinecap: "round",
29
- strokeLinejoin: "round",
30
- className: "size-full"
31
- };
32
- switch (type) {
33
- case "info":
34
- return /* @__PURE__ */ React.createElement("svg", { ...svgProps }, /* @__PURE__ */ React.createElement("circle", { cx: "10", cy: "10", r: "7.25" }), /* @__PURE__ */ React.createElement("path", { d: "M10 9.25v4" }), /* @__PURE__ */ React.createElement("path", { d: "M10 6.75h.01" }));
35
- case "success":
36
- return /* @__PURE__ */ React.createElement("svg", { ...svgProps }, /* @__PURE__ */ React.createElement("circle", { cx: "10", cy: "10", r: "7.25" }), /* @__PURE__ */ React.createElement("path", { d: "m6.75 10.25 2.1 2.1 4.4-4.7" }));
37
- case "warning":
38
- return /* @__PURE__ */ React.createElement("svg", { ...svgProps }, /* @__PURE__ */ React.createElement("path", { d: "M10 3.25 17 16.5H3L10 3.25Z" }), /* @__PURE__ */ React.createElement("path", { d: "M10 8v3.5" }), /* @__PURE__ */ React.createElement("path", { d: "M10 14.25h.01" }));
39
- case "error":
40
- return /* @__PURE__ */ React.createElement("svg", { ...svgProps }, /* @__PURE__ */ React.createElement("circle", { cx: "10", cy: "10", r: "7.25" }), /* @__PURE__ */ React.createElement("path", { d: "M10 6.5v4.25" }), /* @__PURE__ */ React.createElement("path", { d: "M10 13.75h.01" }));
41
- case "loading":
42
- return /* @__PURE__ */ React.createElement("svg", { ...svgProps }, /* @__PURE__ */ React.createElement("circle", { cx: "10", cy: "10", r: "6.75", opacity: "0.25" }), /* @__PURE__ */ React.createElement("path", { d: "M10 3.25a6.75 6.75 0 0 1 6.75 6.75" }));
43
- default:
44
- return null;
45
- }
46
- }
7
+ import { CloseIcon } from "./icons/close.mjs";
8
+ import { ErrorIcon } from "./icons/error.mjs";
9
+ import { InfoIcon } from "./icons/info.mjs";
10
+ import { LoadingIcon } from "./icons/loading.mjs";
11
+ import { SuccessIcon } from "./icons/success.mjs";
12
+ import { WarningIcon } from "./icons/warning.mjs";
13
+ const SWIPE_DIRECTIONS = {
14
+ "top-left": ["up", "left"],
15
+ "top-right": ["up", "right"],
16
+ "bottom-left": ["down", "left"],
17
+ "bottom-right": ["down", "right"]
18
+ };
19
+ const STATUS_ICONS = {
20
+ info: InfoIcon,
21
+ success: SuccessIcon,
22
+ warning: WarningIcon,
23
+ error: ErrorIcon,
24
+ loading: LoadingIcon
25
+ };
47
26
  function ToastStatusIcon({
48
27
  type,
49
28
  className
50
29
  }) {
51
- const content = statusIconContent(type);
52
- if (!content) {
30
+ const Icon = type ? STATUS_ICONS[type] : void 0;
31
+ if (!Icon) {
53
32
  return null;
54
33
  }
55
34
  return /* @__PURE__ */ React.createElement(
@@ -60,61 +39,53 @@ function ToastStatusIcon({
60
39
  "data-icon": type,
61
40
  className
62
41
  },
63
- content
42
+ /* @__PURE__ */ React.createElement(Icon, { className: "size-full" })
64
43
  );
65
44
  }
66
- function ToastList({
67
- placement,
68
- closeLabel
69
- }) {
70
- const { toasts } = useToast();
71
- const ui = theme({ placement });
72
- return toasts.map((toast) => /* @__PURE__ */ React.createElement(
73
- UIToast.Root,
74
- {
75
- key: toast.id,
76
- toast,
77
- "data-slot": "toast",
78
- className: ui.root(),
79
- swipeDirection: placement === "top" ? "up" : "down"
80
- },
81
- /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true", "data-slot": "glass", className: ui.glass() }, /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true", "data-slot": "overlay", className: ui.overlay() })),
82
- /* @__PURE__ */ React.createElement(UIToast.Content, { "data-slot": "content", className: ui.content() }, /* @__PURE__ */ React.createElement(ToastStatusIcon, { type: toast.type, className: ui.icon() }), /* @__PURE__ */ React.createElement("div", { "data-slot": "text", className: ui.text() }, /* @__PURE__ */ React.createElement(UIToast.Title, { "data-slot": "title", className: ui.title() }), /* @__PURE__ */ React.createElement(
83
- UIToast.Description,
84
- {
85
- "data-slot": "description",
86
- className: ui.description()
87
- }
88
- )), /* @__PURE__ */ React.createElement("div", { "data-slot": "actions", className: ui.actions() }, /* @__PURE__ */ React.createElement(UIToast.Action, { "data-slot": "action", className: ui.action() }), /* @__PURE__ */ React.createElement(
89
- UIToast.Close,
90
- {
91
- "data-slot": "close",
92
- className: ui.close(),
93
- "aria-label": closeLabel
94
- },
95
- /* @__PURE__ */ React.createElement(CloseIcon, null)
96
- )))
97
- ));
45
+ function ToastProvider({ children, ...props }) {
46
+ return /* @__PURE__ */ React.createElement(UIToast.Provider, { ...props }, children);
98
47
  }
99
- function ToastProvider({
100
- children,
48
+ function Toast({
101
49
  position = "bottom-right",
102
- closeLabel = "Dismiss",
103
- viewportLabel = "Notifications",
50
+ close,
104
51
  className,
105
52
  ...props
106
53
  }) {
107
- const [placement, align] = position.split("-");
108
- const ui = theme({ placement, align });
109
- return /* @__PURE__ */ React.createElement(UIToast.Provider, { ...props }, children, /* @__PURE__ */ React.createElement(UIToast.Portal, null, /* @__PURE__ */ React.createElement(
54
+ const { toasts } = useToast();
55
+ const ui = theme({ position });
56
+ return /* @__PURE__ */ React.createElement(UIToast.Portal, null, /* @__PURE__ */ React.createElement(
110
57
  UIToast.Viewport,
111
58
  {
112
59
  "data-slot": "viewport",
113
60
  "data-position": position,
114
- "aria-label": viewportLabel,
115
- className: cn(ui.viewport(), className)
61
+ className: ui.viewport()
116
62
  },
117
- /* @__PURE__ */ React.createElement(ToastList, { placement, closeLabel })
118
- )));
63
+ toasts.map((toast) => /* @__PURE__ */ React.createElement(
64
+ UIToast.Root,
65
+ {
66
+ ...props,
67
+ key: toast.id,
68
+ toast,
69
+ "data-slot": "toast",
70
+ className: cn(ui.root(), className),
71
+ swipeDirection: SWIPE_DIRECTIONS[position]
72
+ },
73
+ /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true", "data-slot": "glass", className: ui.glass() }, /* @__PURE__ */ React.createElement(
74
+ "span",
75
+ {
76
+ "aria-hidden": "true",
77
+ "data-slot": "overlay",
78
+ className: ui.overlay()
79
+ }
80
+ )),
81
+ /* @__PURE__ */ React.createElement(UIToast.Content, { "data-slot": "content", className: ui.content() }, /* @__PURE__ */ React.createElement(ToastStatusIcon, { type: toast.type, className: ui.icon() }), /* @__PURE__ */ React.createElement("div", { "data-slot": "text", className: ui.text() }, /* @__PURE__ */ React.createElement(UIToast.Title, { "data-slot": "title", className: ui.title() }), /* @__PURE__ */ React.createElement(
82
+ UIToast.Description,
83
+ {
84
+ "data-slot": "description",
85
+ className: ui.description()
86
+ }
87
+ )), /* @__PURE__ */ React.createElement("div", { "data-slot": "actions", className: ui.actions() }, /* @__PURE__ */ React.createElement(UIToast.Action, { "data-slot": "action", className: ui.action() }), close ? close : /* @__PURE__ */ React.createElement(UIToast.Close, { "data-slot": "close", className: ui.close() }, /* @__PURE__ */ React.createElement(CloseIcon, { className: "size-4" }))))
88
+ ))
89
+ ));
119
90
  }
120
- export { ToastProvider };
91
+ export { Toast, ToastProvider };
package/dist/index.css CHANGED
@@ -1 +1 @@
1
- @import "./keyframes.css";@custom-variant ui-light (&:where(.light, .light *, [data-theme="light"], [data-theme="light"] *));@custom-variant ui-dark (&:where(.dark, .dark *, [data-theme="dark"], [data-theme="dark"] *));@layer theme{:host,:root{--ui-ink:#1f1f1f;--ui-ink-active:#000;--ui-checkbox-mark:#fff;--ui-checkbox-rim:hsla(0,0%,100%,.18);--ui-checkbox-shadow:inset 0 0 0 1px rgba(0,0,0,.04),0 1px 2px rgba(0,0,0,.16);--ui-dialog-surface:#fff;--ui-glass-surface:rgba(0,0,0,.02);--ui-glass-border:rgba(51,51,51,.08);--ui-glass-focus:rgba(0,0,0,.6);--ui-glass-inner-border:rgba(0,0,0,.9);--ui-glass-rim:hsla(0,0%,100%,.2);--ui-glass-highlight:hsla(0,0%,100%,.8);--ui-glass-glare-stop:15%;--ui-glass-tone:brightness(0.9);--ui-glass-contrast:3;--ui-drop-shadow-label:drop-shadow(0 25px 3px hsla(0,0%,40%,.15));--ui-shadow-glass:inset 2px -2px 1px -1px hsla(0,0%,100%,.9),inset -2px 2px 1px -1px hsla(0,0%,100%,.9),inset 6px -6px 1px -6px hsla(0,0%,100%,.55),inset -6px 6px 1px -6px hsla(0,0%,100%,.55),inset 0 0 2px rgba(0,0,0,.8),0 4px 8px rgba(0,0,0,.2)}.dark,[data-theme=dark]{--ui-ink:#f5f5f5;--ui-ink-active:#fff;--ui-checkbox-mark:#141414;--ui-checkbox-rim:hsla(0,0%,100%,.14);--ui-checkbox-shadow:inset 0 0 0 1px hsla(0,0%,100%,.04),0 1px 2px rgba(0,0,0,.4);--ui-dialog-surface:#141414;--ui-glass-surface:hsla(0,0%,100%,.06);--ui-glass-border:hsla(0,0%,100%,.14);--ui-glass-focus:hsla(0,0%,100%,.7);--ui-glass-inner-border:hsla(0,0%,100%,.4);--ui-glass-rim:hsla(0,0%,100%,.25);--ui-glass-highlight:hsla(0,0%,100%,.45);--ui-glass-tone:brightness(0.9);--ui-glass-contrast:1;--ui-drop-shadow-label:drop-shadow(0 25px 3px rgba(0,0,0,.45));--ui-shadow-glass:inset 2px -2px 1px -1px hsla(0,0%,100%,.35),inset -2px 2px 1px -1px hsla(0,0%,100%,.35),inset 6px -6px 1px -6px hsla(0,0%,100%,.2),inset -6px 6px 1px -6px hsla(0,0%,100%,.2),inset 0 0 2px rgba(0,0,0,.9),0 4px 8px rgba(0,0,0,.35)}.light,[data-theme=light]{--ui-ink:#1f1f1f;--ui-ink-active:#000;--ui-checkbox-mark:#fff;--ui-checkbox-rim:hsla(0,0%,100%,.18);--ui-checkbox-shadow:inset 0 0 0 1px rgba(0,0,0,.04),0 1px 2px rgba(0,0,0,.16);--ui-dialog-surface:#fff;--ui-glass-surface:rgba(0,0,0,.02);--ui-glass-border:rgba(51,51,51,.08);--ui-glass-focus:rgba(0,0,0,.6);--ui-glass-inner-border:rgba(0,0,0,.9);--ui-glass-rim:hsla(0,0%,100%,.2);--ui-glass-highlight:hsla(0,0%,100%,.8);--ui-glass-glare-stop:15%;--ui-glass-tone:brightness(0.9);--ui-glass-contrast:3;--ui-drop-shadow-label:drop-shadow(0 25px 3px hsla(0,0%,40%,.15));--ui-shadow-glass:inset 2px -2px 1px -1px hsla(0,0%,100%,.9),inset -2px 2px 1px -1px hsla(0,0%,100%,.9),inset 6px -6px 1px -6px hsla(0,0%,100%,.55),inset -6px 6px 1px -6px hsla(0,0%,100%,.55),inset 0 0 2px rgba(0,0,0,.8),0 4px 8px rgba(0,0,0,.2)}:host,:root{--ui-glass-blur:2px;--ui-glass-saturate:150%;--ui-glass-chip-blur:8px;--ui-glass-inner-inset:8px;--ui-glass-inner-offset:1px;--ui-glass-inner-blur:8px;--ui-glass-highlight-blur:7px;--ui-glass-rim-inset:9px;--ui-glass-rim-blur:1px;--ui-glass-edge-shadow:inset 1px -1px 2px hsla(0,0%,100%,.55),inset -1px 1px 2px rgba(0,0,0,.18),0 1px 3px rgba(0,0,0,.2);--ui-glass-thumb-shadow:0 1px 3px rgba(0,0,0,.2),inset 0 0 0 1px hsla(0,0%,100%,.55);--ui-glass-lens-blur:1.5px;--ui-glass-lens-saturate:160%;--ui-glass-lens-border:hsla(0,0%,100%,.55);--ui-glass-lens-surface:linear-gradient(180deg,hsla(0,0%,100%,.72),hsla(0,0%,100%,.3));--ui-glass-lens-surface-active:hsla(0,0%,100%,.12);--ui-glass-lens-shadow:0 4px 10px rgba(0,30,63,.14),0 0 2px rgba(0,9,20,.12),inset 1px 1px 0 hsla(0,0%,100%,.8),inset -1px -1px 0 rgba(69,168,243,.16);--ui-glass-lens-shadow-active:0 6px 18px rgba(0,30,63,.18),0 0 3px rgba(0,9,20,.14),inset 1px 1px 0 hsla(0,0%,100%,.72),inset -1px -1px 0 rgba(69,168,243,.2);--ui-glass-lens-specular:inset 1px 1px 0 hsla(0,0%,100%,.7),inset 1px 3px 0 rgba(28,63,90,.05),inset 0 0 18px hsla(0,0%,100%,.55),inset -1px -1px 0 rgba(69,168,243,.16);--ui-glass-lens-glare:linear-gradient(180deg,hsla(0,0%,100%,.75),hsla(0,0%,100%,0))}}@theme inline{--color-ui-ink:var(--ui-ink);--color-ui-ink-active:var(--ui-ink-active);--color-ui-dialog-surface:var(--ui-dialog-surface);--color-ui-glass-surface:var(--ui-glass-surface);--color-ui-glass-border:var(--ui-glass-border);--color-ui-glass-focus:var(--ui-glass-focus);--color-ui-glass-inner-border:var(--ui-glass-inner-border);--color-ui-glass-rim:var(--ui-glass-rim);--color-ui-glass-highlight:var(--ui-glass-highlight)}@layer components{.ui-glass{backdrop-filter:blur(var(--ui-glass-blur,2px)) saturate(var(--ui-glass-saturate,150%));background-color:var(--ui-glass-surface);border:1px double var(--ui-glass-border);box-shadow:var(--ui-shadow-glass);filter:var(--ui-glass-tone,brightness(.9)) contrast(var(--ui-glass-contrast,3));position:relative}.ui-glass-inner:before{border:1px solid var(--ui-glass-inner-border);bottom:calc(var(--ui-glass-inner-inset, 8px) - var(--ui-glass-inner-offset, 1px));filter:blur(var(--ui-glass-inner-blur,8px));left:var(--ui-glass-inner-inset,8px);right:var(--ui-glass-inner-inset,8px);top:calc(var(--ui-glass-inner-inset, 8px) + var(--ui-glass-inner-offset, 1px));z-index:1}.ui-glass-highlight:after,.ui-glass-inner:before{border-radius:inherit;content:"";pointer-events:none;position:absolute}.ui-glass-highlight:after{background:linear-gradient(45deg,var(--ui-glass-highlight) 0,transparent var(--ui-glass-glare-stop),transparent calc(100% - var(--ui-glass-glare-stop)),var(--ui-glass-highlight) 100%);filter:blur(var(--ui-glass-highlight-blur,7px));inset:0;z-index:3}.ui-glass-rim{border:1px solid var(--ui-glass-rim);border-radius:inherit;filter:blur(var(--ui-glass-rim-blur,1px));height:calc(100% - var(--ui-glass-rim-inset, 9px));left:50%;pointer-events:none;position:absolute;top:50%;transform:translate(-50%,-50%);width:calc(100% - var(--ui-glass-rim-inset, 9px));z-index:2}.ui-glass-chip{backdrop-filter:blur(var(--ui-glass-chip-blur,8px)) saturate(var(--ui-glass-saturate,150%));background-color:var(--ui-glass-surface);border:1px solid var(--ui-glass-border);box-shadow:inset 0 1px 0 var(--ui-glass-highlight)}}
1
+ @import "./keyframes.css";@source "./**/*.mjs";@source "./**/*.{ts,tsx}";@custom-variant ui-light (&:where(.light, .light *, [data-theme="light"], [data-theme="light"] *));@custom-variant ui-dark (&:where(.dark, .dark *, [data-theme="dark"], [data-theme="dark"] *));@layer theme{:host,:root{--ui-ink:#1f1f1f;--ui-ink-active:#000;--ui-checkbox-mark:#fff;--ui-checkbox-rim:hsla(0,0%,100%,.18);--ui-checkbox-shadow:inset 0 0 0 1px rgba(0,0,0,.04),0 1px 2px rgba(0,0,0,.16);--ui-dialog-surface:#fff;--ui-glass-surface:rgba(0,0,0,.02);--ui-glass-border:rgba(51,51,51,.08);--ui-glass-focus:rgba(0,0,0,.6);--ui-glass-inner-border:rgba(0,0,0,.9);--ui-glass-rim:hsla(0,0%,100%,.2);--ui-glass-highlight:hsla(0,0%,100%,.8);--ui-glass-glare-stop:15%;--ui-glass-tone:brightness(0.9);--ui-glass-contrast:3;--ui-drop-shadow-label:drop-shadow(0 25px 3px hsla(0,0%,40%,.15));--ui-shadow-glass:inset 2px -2px 1px -1px hsla(0,0%,100%,.9),inset -2px 2px 1px -1px hsla(0,0%,100%,.9),inset 6px -6px 1px -6px hsla(0,0%,100%,.55),inset -6px 6px 1px -6px hsla(0,0%,100%,.55),inset 0 0 2px rgba(0,0,0,.8),0 4px 8px rgba(0,0,0,.2)}.dark,[data-theme=dark]{--ui-ink:#f5f5f5;--ui-ink-active:#fff;--ui-checkbox-mark:#141414;--ui-checkbox-rim:hsla(0,0%,100%,.14);--ui-checkbox-shadow:inset 0 0 0 1px hsla(0,0%,100%,.04),0 1px 2px rgba(0,0,0,.4);--ui-dialog-surface:#141414;--ui-glass-surface:hsla(0,0%,100%,.06);--ui-glass-border:hsla(0,0%,100%,.14);--ui-glass-focus:hsla(0,0%,100%,.7);--ui-glass-inner-border:hsla(0,0%,100%,.4);--ui-glass-rim:hsla(0,0%,100%,.25);--ui-glass-highlight:hsla(0,0%,100%,.45);--ui-glass-tone:brightness(0.9);--ui-glass-contrast:1;--ui-drop-shadow-label:drop-shadow(0 25px 3px rgba(0,0,0,.45));--ui-shadow-glass:inset 2px -2px 1px -1px hsla(0,0%,100%,.35),inset -2px 2px 1px -1px hsla(0,0%,100%,.35),inset 6px -6px 1px -6px hsla(0,0%,100%,.2),inset -6px 6px 1px -6px hsla(0,0%,100%,.2),inset 0 0 2px rgba(0,0,0,.9),0 4px 8px rgba(0,0,0,.35)}.light,[data-theme=light]{--ui-ink:#1f1f1f;--ui-ink-active:#000;--ui-checkbox-mark:#fff;--ui-checkbox-rim:hsla(0,0%,100%,.18);--ui-checkbox-shadow:inset 0 0 0 1px rgba(0,0,0,.04),0 1px 2px rgba(0,0,0,.16);--ui-dialog-surface:#fff;--ui-glass-surface:rgba(0,0,0,.02);--ui-glass-border:rgba(51,51,51,.08);--ui-glass-focus:rgba(0,0,0,.6);--ui-glass-inner-border:rgba(0,0,0,.9);--ui-glass-rim:hsla(0,0%,100%,.2);--ui-glass-highlight:hsla(0,0%,100%,.8);--ui-glass-glare-stop:15%;--ui-glass-tone:brightness(0.9);--ui-glass-contrast:3;--ui-drop-shadow-label:drop-shadow(0 25px 3px hsla(0,0%,40%,.15));--ui-shadow-glass:inset 2px -2px 1px -1px hsla(0,0%,100%,.9),inset -2px 2px 1px -1px hsla(0,0%,100%,.9),inset 6px -6px 1px -6px hsla(0,0%,100%,.55),inset -6px 6px 1px -6px hsla(0,0%,100%,.55),inset 0 0 2px rgba(0,0,0,.8),0 4px 8px rgba(0,0,0,.2)}:host,:root{--ui-glass-blur:2px;--ui-glass-saturate:150%;--ui-glass-chip-blur:8px;--ui-glass-inner-inset:8px;--ui-glass-inner-offset:1px;--ui-glass-inner-blur:8px;--ui-glass-highlight-blur:7px;--ui-glass-rim-inset:9px;--ui-glass-rim-blur:1px;--ui-glass-edge-shadow:inset 1px -1px 2px hsla(0,0%,100%,.55),inset -1px 1px 2px rgba(0,0,0,.18),0 1px 3px rgba(0,0,0,.2);--ui-glass-thumb-shadow:0 1px 3px rgba(0,0,0,.2),inset 0 0 0 1px hsla(0,0%,100%,.55);--ui-glass-lens-blur:1.5px;--ui-glass-lens-saturate:160%;--ui-glass-lens-border:hsla(0,0%,100%,.55);--ui-glass-lens-surface:linear-gradient(180deg,hsla(0,0%,100%,.72),hsla(0,0%,100%,.3));--ui-glass-lens-surface-active:hsla(0,0%,100%,.12);--ui-glass-lens-shadow:0 4px 10px rgba(0,30,63,.14),0 0 2px rgba(0,9,20,.12),inset 1px 1px 0 hsla(0,0%,100%,.8),inset -1px -1px 0 rgba(69,168,243,.16);--ui-glass-lens-shadow-active:0 6px 18px rgba(0,30,63,.18),0 0 3px rgba(0,9,20,.14),inset 1px 1px 0 hsla(0,0%,100%,.72),inset -1px -1px 0 rgba(69,168,243,.2);--ui-glass-lens-specular:inset 1px 1px 0 hsla(0,0%,100%,.7),inset 1px 3px 0 rgba(28,63,90,.05),inset 0 0 18px hsla(0,0%,100%,.55),inset -1px -1px 0 rgba(69,168,243,.16);--ui-glass-lens-glare:linear-gradient(180deg,hsla(0,0%,100%,.75),hsla(0,0%,100%,0))}}@theme inline{--color-ui-ink:var(--ui-ink);--color-ui-ink-active:var(--ui-ink-active);--color-ui-dialog-surface:var(--ui-dialog-surface);--color-ui-glass-surface:var(--ui-glass-surface);--color-ui-glass-border:var(--ui-glass-border);--color-ui-glass-focus:var(--ui-glass-focus);--color-ui-glass-inner-border:var(--ui-glass-inner-border);--color-ui-glass-rim:var(--ui-glass-rim);--color-ui-glass-highlight:var(--ui-glass-highlight)}@layer components{.ui-glass{backdrop-filter:blur(var(--ui-glass-blur,2px)) saturate(var(--ui-glass-saturate,150%));background-color:var(--ui-glass-surface);border:1px double var(--ui-glass-border);box-shadow:var(--ui-shadow-glass);filter:var(--ui-glass-tone,brightness(.9)) contrast(var(--ui-glass-contrast,3));position:relative}.ui-glass-inner:before{border:1px solid var(--ui-glass-inner-border);bottom:calc(var(--ui-glass-inner-inset, 8px) - var(--ui-glass-inner-offset, 1px));filter:blur(var(--ui-glass-inner-blur,8px));left:var(--ui-glass-inner-inset,8px);right:var(--ui-glass-inner-inset,8px);top:calc(var(--ui-glass-inner-inset, 8px) + var(--ui-glass-inner-offset, 1px));z-index:1}.ui-glass-highlight:after,.ui-glass-inner:before{border-radius:inherit;content:"";pointer-events:none;position:absolute}.ui-glass-highlight:after{background:linear-gradient(45deg,var(--ui-glass-highlight) 0,transparent var(--ui-glass-glare-stop),transparent calc(100% - var(--ui-glass-glare-stop)),var(--ui-glass-highlight) 100%);filter:blur(var(--ui-glass-highlight-blur,7px));inset:0;z-index:3}.ui-glass-rim{border:1px solid var(--ui-glass-rim);border-radius:inherit;filter:blur(var(--ui-glass-rim-blur,1px));height:calc(100% - var(--ui-glass-rim-inset, 9px));left:50%;pointer-events:none;position:absolute;top:50%;transform:translate(-50%,-50%);width:calc(100% - var(--ui-glass-rim-inset, 9px));z-index:2}.ui-glass-chip{backdrop-filter:blur(var(--ui-glass-chip-blur,8px)) saturate(var(--ui-glass-saturate,150%));background-color:var(--ui-glass-surface);border:1px solid var(--ui-glass-border);box-shadow:inset 0 1px 0 var(--ui-glass-highlight)}}
package/dist/index.d.ts CHANGED
@@ -10,7 +10,7 @@ export { Popover, type PopoverProps } from "./components/popover";
10
10
  export { ScrollArea, type ScrollAreaOrientation, type ScrollAreaProps, } from "./components/scroll-area";
11
11
  export { Slider, type SliderProps } from "./components/slider";
12
12
  export { Switch, type SwitchProps } from "./components/switch";
13
- export { ToastProvider, type ToastPosition, type ToastProviderProps, } from "./components/toast";
13
+ export { Toast, ToastProvider, type ToastPosition, type ToastProps, type ToastProviderProps, } from "./components/toast";
14
14
  export { createToastManager } from "./composables/create-toast-manager";
15
15
  export { useToast, type ToastManager, type ToastManagerAddOptions, type ToastManagerPromiseOptions, type ToastManagerUpdateOptions, type ToastObject, } from "./composables/use-toast";
16
16
  export { Toggle, type ToggleProps } from "./components/toggle";
package/dist/index.mjs CHANGED
@@ -15,6 +15,7 @@ export {
15
15
  export { Slider } from "./components/slider.mjs";
16
16
  export { Switch } from "./components/switch.mjs";
17
17
  export {
18
+ Toast,
18
19
  ToastProvider
19
20
  } from "./components/toast.mjs";
20
21
  export { createToastManager } from "./composables/create-toast-manager.mjs";
@@ -0,0 +1,3 @@
1
+ /** Join space-separated ID references, dropping empty values. */
2
+ declare function mergeIds(...ids: Array<string | null | undefined>): string | undefined;
3
+ export { mergeIds };
@@ -0,0 +1,5 @@
1
+ function mergeIds(...ids) {
2
+ const merged = ids.filter((id) => Boolean(id)).join(" ").trim();
3
+ return merged || void 0;
4
+ }
5
+ export { mergeIds };
@@ -1,22 +1,15 @@
1
1
  declare const _default: import("tailwind-variants").TVReturnType<{
2
- placement: {
3
- top: {
2
+ position: {
3
+ "top-left": {
4
4
  viewport: string;
5
- root: string[];
6
5
  };
7
- bottom: {
6
+ "top-right": {
8
7
  viewport: string;
9
- root: string[];
10
8
  };
11
- };
12
- align: {
13
- left: {
14
- viewport: string;
15
- };
16
- center: {
9
+ "bottom-left": {
17
10
  viewport: string;
18
11
  };
19
- right: {
12
+ "bottom-right": {
20
13
  viewport: string;
21
14
  };
22
15
  };
@@ -34,24 +27,17 @@ declare const _default: import("tailwind-variants").TVReturnType<{
34
27
  action: string[];
35
28
  close: string[];
36
29
  }, undefined, {
37
- placement: {
38
- top: {
30
+ position: {
31
+ "top-left": {
39
32
  viewport: string;
40
- root: string[];
41
33
  };
42
- bottom: {
34
+ "top-right": {
43
35
  viewport: string;
44
- root: string[];
45
36
  };
46
- };
47
- align: {
48
- left: {
37
+ "bottom-left": {
49
38
  viewport: string;
50
39
  };
51
- center: {
52
- viewport: string;
53
- };
54
- right: {
40
+ "bottom-right": {
55
41
  viewport: string;
56
42
  };
57
43
  };
@@ -69,24 +55,17 @@ declare const _default: import("tailwind-variants").TVReturnType<{
69
55
  action: string[];
70
56
  close: string[];
71
57
  }, import("tailwind-variants").TVReturnTypeLike<{
72
- placement: {
73
- top: {
74
- viewport: string;
75
- root: string[];
76
- };
77
- bottom: {
58
+ position: {
59
+ "top-left": {
78
60
  viewport: string;
79
- root: string[];
80
61
  };
81
- };
82
- align: {
83
- left: {
62
+ "top-right": {
84
63
  viewport: string;
85
64
  };
86
- center: {
65
+ "bottom-left": {
87
66
  viewport: string;
88
67
  };
89
- right: {
68
+ "bottom-right": {
90
69
  viewport: string;
91
70
  };
92
71
  };
@@ -14,6 +14,8 @@ export default tv({
14
14
  "data-[expanded]:h-[var(--toast-height)]",
15
15
  "data-[limited]:pointer-events-none data-[limited]:opacity-0",
16
16
  "data-[swiping]:cursor-grabbing",
17
+ "data-[ending-style]:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)_-_150%))]",
18
+ "data-[ending-style]:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)_+_150%))]",
17
19
  "outline-none focus-visible:outline-2 focus-visible:outline-offset-4 focus-visible:outline-ui-glass-focus",
18
20
  "motion-reduce:transition-none"
19
21
  ],
@@ -57,9 +59,25 @@ export default tv({
57
59
  ]
58
60
  },
59
61
  variants: {
60
- placement: {
61
- top: {
62
- viewport: "top-[max(1rem,env(safe-area-inset-top))] bottom-auto",
62
+ position: {
63
+ "top-left": {
64
+ viewport: "top-[max(1rem,env(safe-area-inset-top))] bottom-auto left-[max(1rem,env(safe-area-inset-left))] right-auto"
65
+ },
66
+ "top-right": {
67
+ viewport: "top-[max(1rem,env(safe-area-inset-top))] bottom-auto right-[max(1rem,env(safe-area-inset-right))] left-auto"
68
+ },
69
+ "bottom-left": {
70
+ viewport: "bottom-[max(1rem,env(safe-area-inset-bottom))] top-auto left-[max(1rem,env(safe-area-inset-left))] right-auto"
71
+ },
72
+ "bottom-right": {
73
+ viewport: "bottom-[max(1rem,env(safe-area-inset-bottom))] top-auto right-[max(1rem,env(safe-area-inset-right))] left-auto"
74
+ }
75
+ }
76
+ },
77
+ compoundVariants: [
78
+ {
79
+ position: ["top-left", "top-right"],
80
+ class: {
63
81
  root: [
64
82
  "top-0 origin-top",
65
83
  "[--offset-y:calc(var(--toast-offset-y)_+_(var(--toast-index)*var(--gap))_+_var(--toast-swipe-movement-y))]",
@@ -70,9 +88,11 @@ export default tv({
70
88
  "data-[ending-style]:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)_-_150%))]",
71
89
  "after:absolute after:bottom-full after:left-0 after:h-[calc(var(--gap)_+_1px)] after:w-full after:content-['']"
72
90
  ]
73
- },
74
- bottom: {
75
- viewport: "bottom-[max(1rem,env(safe-area-inset-bottom))] top-auto",
91
+ }
92
+ },
93
+ {
94
+ position: ["bottom-left", "bottom-right"],
95
+ class: {
76
96
  root: [
77
97
  "bottom-0 origin-bottom",
78
98
  "[--offset-y:calc(var(--toast-offset-y)*-1_+_(var(--toast-index)*var(--gap)*-1)_+_var(--toast-swipe-movement-y))]",
@@ -84,21 +104,9 @@ export default tv({
84
104
  "after:absolute after:top-full after:left-0 after:h-[calc(var(--gap)_+_1px)] after:w-full after:content-['']"
85
105
  ]
86
106
  }
87
- },
88
- align: {
89
- left: {
90
- viewport: "left-[max(1rem,env(safe-area-inset-left))] right-auto"
91
- },
92
- center: {
93
- viewport: "left-1/2 -translate-x-1/2"
94
- },
95
- right: {
96
- viewport: "right-[max(1rem,env(safe-area-inset-right))] left-auto"
97
- }
98
107
  }
99
- },
108
+ ],
100
109
  defaultVariants: {
101
- placement: "bottom",
102
- align: "right"
110
+ position: "bottom-right"
103
111
  }
104
112
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@xingxingmofashu/ui",
3
- "version": "0.1.0",
4
- "description": "Liquid Glass UI components for React 19 and Tailwind CSS 4.",
3
+ "version": "0.1.2",
4
+ "description": "Accessible React components with a Liquid Glass visual style for React 19 and Tailwind CSS 4.",
5
5
  "license": "MIT",
6
6
  "author": "xingxingmofashu",
7
7
  "type": "module",