@xingxingmofashu/ui 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <picture>
2
- <source media="(prefers-color-scheme: dark)" srcset=".github/assets/banner-dark.png">
3
- <source media="(prefers-color-scheme: light)" srcset=".github/assets/banner-light.png">
4
- <img alt="@xingxingmofashu/ui: Between black and white, glass finds its light." width="1247" src=".github/assets/banner-light.png">
2
+ <source media="(prefers-color-scheme: dark)" srcset=".github/assets/surface-lab-dark.png">
3
+ <source media="(prefers-color-scheme: light)" srcset=".github/assets/surface-lab-light.png">
4
+ <img alt="The Liquid Glass Surface Lab panel showing glass components over the default image background in light mode." width="1024" src=".github/assets/surface-lab-light.png">
5
5
  </picture>
6
6
 
7
7
  <div align="center">
@@ -20,27 +20,15 @@ components with a Liquid Glass visual style, built with
20
20
  [Tailwind Variants](https://www.tailwind-variants.org). Glass surfaces, rim
21
21
  highlights, and inner shadows are drawn entirely with CSS.
22
22
 
23
- The package ships sixteen components: Accordion, Alert Dialog, Button, Card,
24
- Checkbox, Dialog, Drawer, Input, Popover, Progress, Scroll Area, Slider, Switch,
25
- Toast, Toggle, and Toggle Group.
23
+ The package ships eighteen components: Accordion, Alert Dialog, Button, Card,
24
+ Checkbox, Dialog, Drawer, Input, Popover, Progress, Radio Group, Scroll Area,
25
+ Slider, Switch, Tabs, Toast, Toggle, and Toggle Group.
26
26
 
27
27
  > [!NOTE]
28
28
  > Requires React 19, React DOM 19, Tailwind CSS 4, Node.js 22+, and an
29
29
  > ESM-capable bundler. `Card` is server-safe; the interactive components are
30
30
  > client components.
31
31
 
32
- ## Preview
33
-
34
- <picture>
35
- <source media="(prefers-color-scheme: dark)" srcset=".github/assets/playground-dark.png">
36
- <source media="(prefers-color-scheme: light)" srcset=".github/assets/playground-light.png">
37
- <img alt="The Liquid Glass playground showing live component previews in light mode." width="1440" src=".github/assets/playground-light.png">
38
- </picture>
39
-
40
- Every component is rendered live in the local playground. Run
41
- `pnpm playground:dev`, open <http://localhost:3002>, and switch between light and
42
- dark to compare the same previews in both themes.
43
-
44
32
  ## Documentation
45
33
 
46
34
  - [English](https://xingxingmofashu.github.io/ui/docs/en/get-started/introduction)
@@ -106,57 +94,6 @@ import buttonTheme from "@xingxingmofashu/ui/theme/button";
106
94
 
107
95
  Toast hooks live under `@xingxingmofashu/ui/composables`.
108
96
 
109
- ## Components
110
-
111
- ### Element
112
-
113
- - Accordion
114
- - Button
115
- - Card
116
- - Scroll Area
117
- - Toggle
118
- - Toggle Group
119
-
120
- ### Form
121
-
122
- - Checkbox
123
- - Input
124
- - Slider
125
- - Switch
126
-
127
- ### Overlay
128
-
129
- - Alert Dialog
130
- - Dialog
131
- - Drawer
132
- - Popover
133
- - Toast
134
-
135
- ## Theming
136
-
137
- Light and dark tokens are selected by `.light` / `.dark`, or by
138
- `data-theme="light"` / `data-theme="dark"`:
139
-
140
- ```html
141
- <html class="dark">
142
- ```
143
-
144
- or:
145
-
146
- ```html
147
- <html data-theme="dark">
148
- ```
149
-
150
- A nested `.light` / `[data-theme="light"]` subtree can switch back to light
151
- tokens. Shared colors and glass parameters live in the `--ui-*` tokens; override
152
- them in your own theme layer. Tailwind aliases use the `ui-` prefix
153
- (`text-ui-ink`, `outline-ui-glass-focus`, and so on) to avoid colliding with
154
- host tokens.
155
-
156
- The components inherit the host font. The visual material relies on modern CSS:
157
- `backdrop-filter`, `color-mix`, container units, individual `translate` /
158
- `scale` properties, and `:has`. Use a current evergreen browser.
159
-
160
97
  ## Development
161
98
 
162
99
  ```bash
@@ -164,6 +101,8 @@ pnpm install
164
101
  pnpm build # build dist with unbuild
165
102
  pnpm lint # lint with oxlint
166
103
  pnpm test # run the Vitest suite
104
+ pnpm test:visual # run the Playwright visual regression suite
105
+ pnpm test:visual:update # refresh the Surface Lab screenshots
167
106
  pnpm typecheck # typecheck the package
168
107
  pnpm docs:dev # docs site on http://localhost:3001
169
108
  pnpm playground:dev # component playground on http://localhost:3002
@@ -5,7 +5,7 @@ import { type ComponentConfig } from "../internal/tv";
5
5
  import theme from "../theme/accordion";
6
6
  type Accordion = ComponentConfig<typeof theme>;
7
7
  /** One panel managed by the accordion. */
8
- type AccordionItemProps<Value extends string = string> = Omit<UIAccordion.Item.Props, "children" | "className" | "style" | "title" | "value"> & {
8
+ type AccordionItemProps<Value extends string = string> = Omit<UIAccordion.Item.Props, "children" | "className" | "content" | "style" | "title" | "value"> & {
9
9
  /** Value that identifies the item in the accordion value. */
10
10
  value: Value;
11
11
  /** Visible heading inside the trigger. */
@@ -0,0 +1,35 @@
1
+ import { Radio as UIRadio } from "@base-ui/react/radio";
2
+ import { RadioGroup as UIRadioGroup } from "@base-ui/react/radio-group";
3
+ import * as React from "react";
4
+ import { type VariantProps } from "tailwind-variants";
5
+ import { type ComponentConfig } from "../internal/tv";
6
+ import theme from "../theme/radio-group";
7
+ type RadioGroup = ComponentConfig<typeof theme>;
8
+ /** One option managed by the radio group. */
9
+ type RadioGroupItemProps<Value extends string = string> = Omit<UIRadio.Root.Props<Value>, "className" | "children" | "style" | "value"> & {
10
+ /** Value that identifies the option in the group value. */
11
+ value: Value;
12
+ /** Visible label rendered next to the control. */
13
+ label?: React.ReactNode;
14
+ /** Supporting text rendered under the label. */
15
+ description?: React.ReactNode;
16
+ /** Tailwind classes merged into the item root. */
17
+ className?: string;
18
+ /** Tailwind classes for this item's internal slots, keyed by `data-slot`. */
19
+ ui?: Partial<Pick<RadioGroup["slots"], "item" | "box" | "glass" | "overlay" | "indicator" | "dot" | "wrapper" | "itemLabel" | "itemDescription">>;
20
+ };
21
+ type RadioGroupProps<Value extends string = string> = Omit<UIRadioGroup.Props<Value>, "className" | "style" | "children"> & VariantProps<typeof theme> & {
22
+ /** Options rendered by the radio group. */
23
+ items: readonly RadioGroupItemProps<Value>[];
24
+ /** Visible label for the group. */
25
+ label?: React.ReactNode;
26
+ /** Supporting text rendered under the group label. */
27
+ description?: React.ReactNode;
28
+ /** Tailwind classes merged into the group root. */
29
+ className?: string;
30
+ /** Tailwind classes for internal slots, keyed by `data-slot`. */
31
+ ui?: RadioGroup["slots"];
32
+ };
33
+ declare function RadioGroup<Value extends string = string>({ className, ui, size, items, label, description, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, ...props }: RadioGroupProps<Value>): React.JSX.Element;
34
+ export { RadioGroup };
35
+ export type { RadioGroupItemProps, RadioGroupProps };
@@ -0,0 +1,156 @@
1
+ "use client";
2
+ import { Radio as UIRadio } from "@base-ui/react/radio";
3
+ import { RadioGroup as UIRadioGroup } from "@base-ui/react/radio-group";
4
+ import { cn } from "cn";
5
+ import * as React from "react";
6
+ import { mergeIds } from "../internal/aria.mjs";
7
+ import theme from "../theme/radio-group.mjs";
8
+ function RadioGroup({
9
+ className,
10
+ ui,
11
+ size,
12
+ items,
13
+ label,
14
+ description,
15
+ "aria-label": ariaLabel,
16
+ "aria-labelledby": ariaLabelledBy,
17
+ "aria-describedby": ariaDescribedBy,
18
+ ...props
19
+ }) {
20
+ const styles = theme({ size });
21
+ const labelId = React.useId();
22
+ const descriptionId = React.useId();
23
+ const hasName = Boolean(label || ariaLabel || ariaLabelledBy);
24
+ const labelledBy = ariaLabelledBy || (label && !ariaLabel ? labelId : void 0);
25
+ const describedBy = mergeIds(
26
+ ariaDescribedBy,
27
+ description && hasName ? descriptionId : void 0
28
+ );
29
+ return /* @__PURE__ */ React.createElement(
30
+ UIRadioGroup,
31
+ {
32
+ ...props,
33
+ "aria-describedby": describedBy,
34
+ "aria-label": ariaLabel,
35
+ "aria-labelledby": labelledBy,
36
+ "data-slot": "root",
37
+ className: cn(styles.root(), ui?.root, className)
38
+ },
39
+ label || description ? /* @__PURE__ */ React.createElement("div", { "data-slot": "header", className: cn(styles.header(), ui?.header) }, label ? /* @__PURE__ */ React.createElement(
40
+ "span",
41
+ {
42
+ id: labelId,
43
+ "data-slot": "label",
44
+ className: cn(styles.label(), ui?.label)
45
+ },
46
+ label
47
+ ) : null, description ? /* @__PURE__ */ React.createElement(
48
+ "span",
49
+ {
50
+ id: descriptionId,
51
+ "data-slot": "description",
52
+ className: cn(styles.description(), ui?.description)
53
+ },
54
+ description
55
+ ) : null) : null,
56
+ /* @__PURE__ */ React.createElement("div", { "data-slot": "items", className: cn(styles.items(), ui?.items) }, items.map((item) => /* @__PURE__ */ React.createElement(
57
+ RadioGroupItem,
58
+ {
59
+ key: item.value,
60
+ ...item,
61
+ ui: {
62
+ item: cn(ui?.item, item.ui?.item),
63
+ box: cn(ui?.box, item.ui?.box),
64
+ glass: cn(ui?.glass, item.ui?.glass),
65
+ overlay: cn(ui?.overlay, item.ui?.overlay),
66
+ indicator: cn(ui?.indicator, item.ui?.indicator),
67
+ dot: cn(ui?.dot, item.ui?.dot),
68
+ wrapper: cn(
69
+ styles.wrapper(),
70
+ ui?.wrapper,
71
+ item.ui?.wrapper
72
+ ),
73
+ itemLabel: cn(ui?.itemLabel, item.ui?.itemLabel),
74
+ itemDescription: cn(
75
+ ui?.itemDescription,
76
+ item.ui?.itemDescription
77
+ )
78
+ }
79
+ }
80
+ )))
81
+ );
82
+ }
83
+ function RadioGroupItem({
84
+ className,
85
+ ui,
86
+ label,
87
+ description,
88
+ "aria-label": ariaLabel,
89
+ "aria-labelledby": ariaLabelledBy,
90
+ "aria-describedby": ariaDescribedBy,
91
+ ...props
92
+ }) {
93
+ const styles = theme();
94
+ const labelId = React.useId();
95
+ const descriptionId = React.useId();
96
+ const hasName = Boolean(label || ariaLabel || ariaLabelledBy);
97
+ const labelledBy = ariaLabelledBy || (label && !ariaLabel ? labelId : void 0);
98
+ const describedBy = mergeIds(
99
+ ariaDescribedBy,
100
+ description && hasName ? descriptionId : void 0
101
+ );
102
+ return /* @__PURE__ */ React.createElement(
103
+ UIRadio.Root,
104
+ {
105
+ ...props,
106
+ "aria-describedby": describedBy,
107
+ "aria-label": ariaLabel,
108
+ "aria-labelledby": labelledBy,
109
+ "data-slot": "item",
110
+ className: cn(styles.item(), ui?.item, className)
111
+ },
112
+ /* @__PURE__ */ React.createElement("span", { "data-slot": "box", className: cn(styles.box(), ui?.box) }, /* @__PURE__ */ React.createElement(
113
+ "span",
114
+ {
115
+ "aria-hidden": "true",
116
+ "data-slot": "glass",
117
+ className: cn(styles.glass(), ui?.glass)
118
+ },
119
+ /* @__PURE__ */ React.createElement(
120
+ "span",
121
+ {
122
+ "aria-hidden": "true",
123
+ "data-slot": "overlay",
124
+ className: cn(styles.overlay(), ui?.overlay)
125
+ }
126
+ )
127
+ ), /* @__PURE__ */ React.createElement(
128
+ UIRadio.Indicator,
129
+ {
130
+ keepMounted: true,
131
+ "aria-hidden": "true",
132
+ "data-slot": "indicator",
133
+ className: cn(styles.indicator(), ui?.indicator)
134
+ },
135
+ /* @__PURE__ */ React.createElement("span", { "data-slot": "dot", className: cn(styles.dot(), ui?.dot) })
136
+ )),
137
+ label || description ? /* @__PURE__ */ React.createElement("span", { "data-slot": "wrapper", className: cn(styles.wrapper(), ui?.wrapper) }, label ? /* @__PURE__ */ React.createElement(
138
+ "span",
139
+ {
140
+ id: labelId,
141
+ "data-slot": "item-label",
142
+ className: cn(styles.itemLabel(), ui?.itemLabel)
143
+ },
144
+ label
145
+ ) : null, description ? /* @__PURE__ */ React.createElement(
146
+ "span",
147
+ {
148
+ id: descriptionId,
149
+ "data-slot": "item-description",
150
+ className: cn(styles.itemDescription(), ui?.itemDescription)
151
+ },
152
+ description
153
+ ) : null) : null
154
+ );
155
+ }
156
+ export { RadioGroup };
@@ -0,0 +1,46 @@
1
+ import { Tabs as UITabs } from "@base-ui/react/tabs";
2
+ import * as React from "react";
3
+ import { type VariantProps } from "tailwind-variants";
4
+ import { type ComponentConfig } from "../internal/tv";
5
+ import theme from "../theme/tabs";
6
+ type Tabs = ComponentConfig<typeof theme>;
7
+ /** One tab and its related panel. */
8
+ type TabsItemProps<Value extends string = string> = Omit<UITabs.Tab.Props, "children" | "className" | "content" | "style" | "value"> & {
9
+ /** Value that identifies the tab and its panel. */
10
+ value: Value;
11
+ /** Visible label inside the tab. */
12
+ label?: React.ReactNode;
13
+ /** Decorative icon rendered before the label. */
14
+ icon?: React.ReactNode;
15
+ /** Panel content shown while the tab is active. */
16
+ content?: React.ReactNode;
17
+ /** Whether to keep the panel mounted while another tab is active. */
18
+ keepMounted?: boolean;
19
+ /** Tailwind classes merged into the tab button. */
20
+ className?: string;
21
+ /** Tailwind classes for this item's internal slots, keyed by `data-slot`. */
22
+ ui?: Partial<Pick<Tabs["slots"], "tab" | "icon" | "label" | "panel" | "panelContent">>;
23
+ };
24
+ type TabsProps<Value extends string = string> = Omit<UITabs.Root.Props, "children" | "className" | "style" | "value" | "defaultValue" | "onValueChange"> & VariantProps<typeof theme> & {
25
+ /** Tabs and panels rendered by the component. */
26
+ items: readonly TabsItemProps<Value>[];
27
+ /** Controlled active value. Use `null` to render no active tab. */
28
+ value?: Value | null;
29
+ /** Initial active value when uncontrolled. */
30
+ defaultValue?: Value | null;
31
+ /** Called when the active value changes. */
32
+ onValueChange?: (value: Value, details: UITabs.Root.ChangeEventDetails) => void;
33
+ /** Whether arrow keys activate a tab as focus moves. */
34
+ activateOnFocus?: boolean;
35
+ /** Whether keyboard focus loops back to the first tab. */
36
+ loopFocus?: boolean;
37
+ /** Disables every tab. */
38
+ disabled?: boolean;
39
+ /** Tailwind classes merged into the root. */
40
+ className?: string;
41
+ /** Tailwind classes for internal slots, keyed by `data-slot`. */
42
+ ui?: Tabs["slots"];
43
+ };
44
+ declare function Tabs<Value extends string = string>({ className, ui, size, items, value, defaultValue, onValueChange, activateOnFocus, loopFocus, disabled, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, ...props }: TabsProps<Value>): React.JSX.Element;
45
+ export { Tabs };
46
+ export type { TabsItemProps, TabsProps };
@@ -0,0 +1,136 @@
1
+ "use client";
2
+ import { Tabs as UITabs } from "@base-ui/react/tabs";
3
+ import { cn } from "cn";
4
+ import * as React from "react";
5
+ import theme from "../theme/tabs.mjs";
6
+ function Tabs({
7
+ className,
8
+ ui,
9
+ size,
10
+ items,
11
+ value,
12
+ defaultValue,
13
+ onValueChange,
14
+ activateOnFocus,
15
+ loopFocus,
16
+ disabled,
17
+ "aria-label": ariaLabel,
18
+ "aria-labelledby": ariaLabelledBy,
19
+ "aria-describedby": ariaDescribedBy,
20
+ ...props
21
+ }) {
22
+ const styles = theme({ size });
23
+ const initialValue = defaultValue !== void 0 ? defaultValue : items.find((item) => !item.disabled)?.value;
24
+ return /* @__PURE__ */ React.createElement(
25
+ UITabs.Root,
26
+ {
27
+ ...props,
28
+ value,
29
+ defaultValue: initialValue,
30
+ onValueChange,
31
+ "data-slot": "root",
32
+ "data-disabled": disabled || void 0,
33
+ className: cn(styles.root(), ui?.root, className)
34
+ },
35
+ /* @__PURE__ */ React.createElement(
36
+ UITabs.List,
37
+ {
38
+ activateOnFocus,
39
+ loopFocus,
40
+ "aria-label": ariaLabel,
41
+ "aria-labelledby": ariaLabelledBy,
42
+ "aria-describedby": ariaDescribedBy,
43
+ "data-slot": "list",
44
+ className: cn(styles.list(), ui?.list)
45
+ },
46
+ /* @__PURE__ */ React.createElement(
47
+ "span",
48
+ {
49
+ "aria-hidden": "true",
50
+ "data-slot": "glass",
51
+ className: cn(styles.glass(), ui?.glass)
52
+ },
53
+ /* @__PURE__ */ React.createElement(
54
+ "span",
55
+ {
56
+ "aria-hidden": "true",
57
+ "data-slot": "overlay",
58
+ className: cn(styles.overlay(), ui?.overlay)
59
+ }
60
+ )
61
+ ),
62
+ /* @__PURE__ */ React.createElement(
63
+ UITabs.Indicator,
64
+ {
65
+ "data-slot": "indicator",
66
+ className: cn(styles.indicator(), ui?.indicator)
67
+ }
68
+ ),
69
+ items.map(
70
+ ({ content: _content, keepMounted: _keepMounted, ui: itemUi, ...item }) => /* @__PURE__ */ React.createElement(
71
+ TabsItem,
72
+ {
73
+ key: item.value,
74
+ ...item,
75
+ disabled: disabled || item.disabled,
76
+ ui: {
77
+ tab: cn(ui?.tab, itemUi?.tab),
78
+ icon: cn(ui?.icon, itemUi?.icon),
79
+ label: cn(ui?.label, itemUi?.label)
80
+ }
81
+ }
82
+ )
83
+ )
84
+ ),
85
+ /* @__PURE__ */ React.createElement("div", { "data-slot": "panels", className: cn(styles.panels(), ui?.panels) }, items.map((item) => /* @__PURE__ */ React.createElement(
86
+ UITabs.Panel,
87
+ {
88
+ key: item.value,
89
+ value: item.value,
90
+ keepMounted: item.keepMounted,
91
+ "data-slot": "panel",
92
+ className: cn(styles.panel(), ui?.panel, item.ui?.panel)
93
+ },
94
+ /* @__PURE__ */ React.createElement(
95
+ "div",
96
+ {
97
+ "data-slot": "panel-content",
98
+ className: cn(
99
+ styles.panelContent(),
100
+ ui?.panelContent,
101
+ item.ui?.panelContent
102
+ )
103
+ },
104
+ item.content
105
+ )
106
+ )))
107
+ );
108
+ }
109
+ function TabsItem({
110
+ className,
111
+ ui,
112
+ label,
113
+ icon,
114
+ ...props
115
+ }) {
116
+ const styles = theme();
117
+ return /* @__PURE__ */ React.createElement(
118
+ UITabs.Tab,
119
+ {
120
+ ...props,
121
+ "data-slot": "tab",
122
+ className: cn(styles.tab(), ui?.tab, className)
123
+ },
124
+ icon ? /* @__PURE__ */ React.createElement(
125
+ "span",
126
+ {
127
+ "aria-hidden": "true",
128
+ "data-slot": "icon",
129
+ className: cn(styles.icon(), ui?.icon)
130
+ },
131
+ icon
132
+ ) : null,
133
+ label !== void 0 && label !== null ? /* @__PURE__ */ React.createElement("span", { "data-slot": "label", className: cn(styles.label(), ui?.label) }, label) : null
134
+ );
135
+ }
136
+ export { Tabs };
@@ -3,5 +3,9 @@ import { Toast as UIToast } from "@base-ui/react/toast";
3
3
  * Returns the toast manager connected to the nearest `ToastProvider`.
4
4
  */
5
5
  declare const useToast: typeof UIToast.useToastManager;
6
- export { useToast };
6
+ /**
7
+ * Creates a toast manager for adding notifications outside the React tree.
8
+ */
9
+ declare const createToastManager: typeof UIToast.createToastManager;
10
+ export { createToastManager, useToast };
7
11
  export type { ToastManager, ToastManagerAddOptions, ToastManagerPromiseOptions, ToastManagerUpdateOptions, ToastObject, } from "@base-ui/react/toast";
@@ -1,4 +1,5 @@
1
1
  "use client";
2
2
  import { Toast as UIToast } from "@base-ui/react/toast";
3
3
  const useToast = UIToast.useToastManager;
4
- export { useToast };
4
+ const createToastManager = UIToast.createToastManager;
5
+ export { createToastManager, useToast };
package/dist/index.css CHANGED
@@ -1 +1 @@
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-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-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-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}}@theme inline{--color-ui-ink:var(--ui-ink);--color-ui-ink-active:var(--ui-ink-active);--color-ui-surface:var(--ui-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-surface:#fff;--ui-glass-surface:hsla(0,0%,100%,.2);--ui-glass-border:rgba(31,31,31,.16);--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.98);--ui-glass-contrast:1.05;--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-surface:#141414;--ui-glass-surface:hsla(0,0%,8%,.62);--ui-glass-border:hsla(0,0%,100%,.2);--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.95);--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-surface:#fff;--ui-glass-surface:hsla(0,0%,100%,.2);--ui-glass-border:rgba(31,31,31,.16);--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.98);--ui-glass-contrast:1.05;--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:4px;--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}}@theme inline{--color-ui-ink:var(--ui-ink);--color-ui-ink-active:var(--ui-ink-active);--color-ui-surface:var(--ui-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,4px)) 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(.98)) contrast(var(--ui-glass-contrast,1.05));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
@@ -8,11 +8,12 @@ export { Drawer, type DrawerProps } from "./components/drawer";
8
8
  export { Input, type InputProps } from "./components/input";
9
9
  export { Popover, type PopoverProps } from "./components/popover";
10
10
  export { Progress, type ProgressProps } from "./components/progress";
11
+ export { RadioGroup, type RadioGroupItemProps, type RadioGroupProps, } from "./components/radio-group";
11
12
  export { ScrollArea, type ScrollAreaOrientation, type ScrollAreaProps, } from "./components/scroll-area";
12
13
  export { Slider, type SliderProps } from "./components/slider";
13
14
  export { Switch, type SwitchProps } from "./components/switch";
15
+ export { Tabs, type TabsItemProps, type TabsProps } from "./components/tabs";
14
16
  export { Toast, ToastProvider, type ToastPosition, type ToastProps, type ToastProviderProps, } from "./components/toast";
15
- export { createToastManager } from "./composables/create-toast-manager";
16
- export { useToast, type ToastManager, type ToastManagerAddOptions, type ToastManagerPromiseOptions, type ToastManagerUpdateOptions, type ToastObject, } from "./composables/use-toast";
17
+ export { createToastManager, useToast, type ToastManager, type ToastManagerAddOptions, type ToastManagerPromiseOptions, type ToastManagerUpdateOptions, type ToastObject, } from "./composables/use-toast";
17
18
  export { Toggle, type ToggleProps } from "./components/toggle";
18
19
  export { ToggleGroup, type ToggleGroupItemProps, type ToggleGroupProps, } from "./components/toggle-group";
package/dist/index.mjs CHANGED
@@ -10,17 +10,21 @@ export { Drawer } from "./components/drawer.mjs";
10
10
  export { Input } from "./components/input.mjs";
11
11
  export { Popover } from "./components/popover.mjs";
12
12
  export { Progress } from "./components/progress.mjs";
13
+ export {
14
+ RadioGroup
15
+ } from "./components/radio-group.mjs";
13
16
  export {
14
17
  ScrollArea
15
18
  } from "./components/scroll-area.mjs";
16
19
  export { Slider } from "./components/slider.mjs";
17
20
  export { Switch } from "./components/switch.mjs";
21
+ export { Tabs } from "./components/tabs.mjs";
18
22
  export {
19
23
  Toast,
20
24
  ToastProvider
21
25
  } from "./components/toast.mjs";
22
- export { createToastManager } from "./composables/create-toast-manager.mjs";
23
26
  export {
27
+ createToastManager,
24
28
  useToast
25
29
  } from "./composables/use-toast.mjs";
26
30
  export { Toggle } from "./components/toggle.mjs";
@@ -7,10 +7,6 @@ type ComponentSlots<T extends {
7
7
  }> = Id<{
8
8
  [K in keyof T["slots"]]?: ClassValue;
9
9
  }>;
10
- /**
11
- * Minimal tailwind-variants component config used to derive prop types from a
12
- * theme recipe. Mirrors Nuxt UI's `ComponentConfig` slot mapping.
13
- */
14
10
  export type ComponentConfig<T extends Record<string, any>> = {
15
11
  slots: ComponentSlots<T>;
16
12
  };
@@ -29,67 +29,5 @@ declare const _default: import("tailwind-variants").TVReturnType<{
29
29
  indicatorIcon: string;
30
30
  panel: string[];
31
31
  panelContent: string[];
32
- }, undefined, {
33
- size: {
34
- xs: {
35
- root: string;
36
- };
37
- sm: {
38
- root: string;
39
- };
40
- md: {
41
- root: string;
42
- };
43
- lg: {
44
- root: string;
45
- };
46
- xl: {
47
- root: string;
48
- };
49
- };
50
- }, {
51
- root: string[];
52
- glass: string[];
53
- overlay: string;
54
- content: string;
55
- item: string[];
56
- header: string;
57
- trigger: string[];
58
- title: string[];
59
- indicator: string[];
60
- indicatorIcon: string;
61
- panel: string[];
62
- panelContent: string[];
63
- }, import("tailwind-variants").TVReturnTypeLike<{
64
- size: {
65
- xs: {
66
- root: string;
67
- };
68
- sm: {
69
- root: string;
70
- };
71
- md: {
72
- root: string;
73
- };
74
- lg: {
75
- root: string;
76
- };
77
- xl: {
78
- root: string;
79
- };
80
- };
81
- }, {
82
- root: string[];
83
- glass: string[];
84
- overlay: string;
85
- content: string;
86
- item: string[];
87
- header: string;
88
- trigger: string[];
89
- title: string[];
90
- indicator: string[];
91
- indicatorIcon: string;
92
- panel: string[];
93
- panelContent: string[];
94
- }>>;
32
+ }, undefined, undefined, undefined, undefined>;
95
33
  export default _default;