@xingxingmofashu/ui 0.1.2 → 0.1.4

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 (54) hide show
  1. package/README.md +12 -60
  2. package/dist/components/accordion.d.ts +14 -10
  3. package/dist/components/accordion.mjs +78 -20
  4. package/dist/components/alert-dialog.d.ts +7 -2
  5. package/dist/components/alert-dialog.mjs +74 -33
  6. package/dist/components/button.d.ts +6 -2
  7. package/dist/components/button.mjs +20 -11
  8. package/dist/components/card.d.ts +6 -1
  9. package/dist/components/card.mjs +27 -4
  10. package/dist/components/checkbox.d.ts +5 -1
  11. package/dist/components/checkbox.mjs +53 -14
  12. package/dist/components/dialog.d.ts +7 -2
  13. package/dist/components/dialog.mjs +66 -20
  14. package/dist/components/drawer.d.ts +7 -2
  15. package/dist/components/drawer.mjs +66 -20
  16. package/dist/components/input.d.ts +5 -1
  17. package/dist/components/input.mjs +60 -33
  18. package/dist/components/popover.d.ts +7 -2
  19. package/dist/components/popover.mjs +53 -14
  20. package/dist/components/progress.d.ts +21 -0
  21. package/dist/components/progress.mjs +71 -0
  22. package/dist/components/scroll-area.d.ts +7 -2
  23. package/dist/components/scroll-area.mjs +48 -14
  24. package/dist/components/slider.d.ts +7 -2
  25. package/dist/components/slider.mjs +38 -22
  26. package/dist/components/switch.d.ts +5 -1
  27. package/dist/components/switch.mjs +47 -23
  28. package/dist/components/toast.d.ts +6 -1
  29. package/dist/components/toast.mjs +68 -15
  30. package/dist/components/toggle-group.d.ts +11 -5
  31. package/dist/components/toggle-group.mjs +54 -6
  32. package/dist/components/toggle.d.ts +5 -1
  33. package/dist/components/toggle.mjs +36 -5
  34. package/dist/composables/use-toast.d.ts +5 -1
  35. package/dist/composables/use-toast.mjs +2 -1
  36. package/dist/index.css +1 -1
  37. package/dist/index.d.ts +2 -2
  38. package/dist/index.mjs +2 -1
  39. package/dist/internal/tv.d.ts +13 -0
  40. package/dist/internal/tv.mjs +0 -0
  41. package/dist/keyframes.css +1 -1
  42. package/dist/theme/alert-dialog.mjs +1 -1
  43. package/dist/theme/checkbox.mjs +4 -4
  44. package/dist/theme/dialog.mjs +1 -1
  45. package/dist/theme/drawer.mjs +1 -1
  46. package/dist/theme/popover.mjs +2 -2
  47. package/dist/theme/progress.d.ts +80 -0
  48. package/dist/theme/progress.mjs +64 -0
  49. package/dist/theme/slider.mjs +8 -0
  50. package/dist/theme/switch.mjs +2 -0
  51. package/dist/theme/toast.mjs +1 -1
  52. package/package.json +7 -2
  53. package/dist/composables/create-toast-manager.d.ts +0 -6
  54. package/dist/composables/create-toast-manager.mjs +0 -3
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">
@@ -15,14 +15,14 @@
15
15
  </div>
16
16
 
17
17
  `@xingxingmofashu/ui` is a collection of accessible, typed [React](https://react.dev)
18
- components with a Liquid Glass visual style, built on [Base UI](https://base-ui.com),
19
- [Tailwind CSS](https://tailwindcss.com), and
18
+ components with a Liquid Glass visual style, built with
19
+ [Tailwind CSS](https://tailwindcss.com) and
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 fifteen components: Accordion, Alert Dialog, Button, Card,
24
- Checkbox, Dialog, Drawer, Input, Popover, Scroll Area, Slider, Switch, Toast,
25
- Toggle, and Toggle Group.
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.
26
26
 
27
27
  > [!NOTE]
28
28
  > Requires React 19, React DOM 19, Tailwind CSS 4, Node.js 22+, and an
@@ -33,6 +33,10 @@ Toggle, and Toggle Group.
33
33
 
34
34
  - [English](https://xingxingmofashu.github.io/ui/docs/en/get-started/introduction)
35
35
  - [简体中文](https://xingxingmofashu.github.io/ui/docs/zh/get-started/introduction)
36
+ - [llms.txt](https://xingxingmofashu.github.io/ui/llms.txt) — a compact index for
37
+ language models and agents
38
+ - [llms-full.txt](https://xingxingmofashu.github.io/ui/llms-full.txt) — every
39
+ guide and component page in one file
36
40
 
37
41
  ## Installation
38
42
 
@@ -90,57 +94,6 @@ import buttonTheme from "@xingxingmofashu/ui/theme/button";
90
94
 
91
95
  Toast hooks live under `@xingxingmofashu/ui/composables`.
92
96
 
93
- ## Components
94
-
95
- ### Element
96
-
97
- - Accordion
98
- - Button
99
- - Card
100
- - Scroll Area
101
- - Toggle
102
- - Toggle Group
103
-
104
- ### Form
105
-
106
- - Checkbox
107
- - Input
108
- - Slider
109
- - Switch
110
-
111
- ### Overlay
112
-
113
- - Alert Dialog
114
- - Dialog
115
- - Drawer
116
- - Popover
117
- - Toast
118
-
119
- ## Theming
120
-
121
- Light and dark tokens are selected by `.light` / `.dark`, or by
122
- `data-theme="light"` / `data-theme="dark"`:
123
-
124
- ```html
125
- <html class="dark">
126
- ```
127
-
128
- or:
129
-
130
- ```html
131
- <html data-theme="dark">
132
- ```
133
-
134
- A nested `.light` / `[data-theme="light"]` subtree can switch back to light
135
- tokens. Shared colors and glass parameters live in the `--ui-*` tokens; override
136
- them in your own theme layer. Tailwind aliases use the `ui-` prefix
137
- (`text-ui-ink`, `outline-ui-glass-focus`, and so on) to avoid colliding with
138
- host tokens.
139
-
140
- The components inherit the host font. The visual material relies on modern CSS:
141
- `backdrop-filter`, `color-mix`, container units, individual `translate` /
142
- `scale` properties, and `:has`. Use a current evergreen browser.
143
-
144
97
  ## Development
145
98
 
146
99
  ```bash
@@ -169,7 +122,6 @@ a reproduction. Before opening a pull request, run `pnpm lint`, `pnpm test`, and
169
122
 
170
123
  ## Credits
171
124
 
172
- - [Base UI](https://base-ui.com)
173
125
  - [Tailwind CSS](https://tailwindcss.com)
174
126
  - [Tailwind Variants](https://www.tailwind-variants.org)
175
127
 
@@ -1,28 +1,32 @@
1
1
  import { Accordion as UIAccordion } from "@base-ui/react/accordion";
2
2
  import * as React from "react";
3
3
  import { type VariantProps } from "tailwind-variants";
4
+ import { type ComponentConfig } from "../internal/tv";
4
5
  import theme from "../theme/accordion";
6
+ type Accordion = ComponentConfig<typeof theme>;
5
7
  /** One panel managed by the accordion. */
6
- type AccordionItemProps = {
8
+ type AccordionItemProps<Value extends string = string> = Omit<UIAccordion.Item.Props, "children" | "className" | "style" | "title" | "value"> & {
7
9
  /** Value that identifies the item in the accordion value. */
8
- value: string;
10
+ value: Value;
9
11
  /** Visible heading inside the trigger. */
10
12
  title: React.ReactNode;
11
13
  /** Panel content. */
12
14
  content?: React.ReactNode;
13
- /** Disables this item. */
14
- disabled?: boolean;
15
- /** Called when this panel opens or closes. */
16
- onOpenChange?: UIAccordion.Item.Props["onOpenChange"];
15
+ /** Replaces the default plus icon. Set to `false` to hide the indicator. */
16
+ indicator?: React.ReactNode | false;
17
17
  /** Tailwind classes merged into the item root. */
18
- className?: any;
18
+ className?: string;
19
+ /** Tailwind classes for this item's internal slots, keyed by `data-slot`. */
20
+ ui?: Partial<Pick<Accordion["slots"], "item" | "header" | "trigger" | "title" | "indicator" | "indicatorIcon" | "panel" | "panelContent">>;
19
21
  };
20
22
  type AccordionProps<Value extends string = string> = Omit<UIAccordion.Root.Props<Value>, "className" | "style" | "children"> & VariantProps<typeof theme> & {
21
23
  /** Panels rendered by the accordion. */
22
- items: readonly AccordionItemProps[];
24
+ items: readonly AccordionItemProps<Value>[];
23
25
  /** Tailwind classes merged into the root. */
24
- className?: any;
26
+ className?: string;
27
+ /** Tailwind classes for internal slots, keyed by `data-slot`. */
28
+ ui?: Accordion["slots"];
25
29
  };
26
- declare function Accordion<Value extends string = string>({ className, size, items, ...props }: AccordionProps<Value>): React.JSX.Element;
30
+ declare function Accordion<Value extends string = string>({ className, ui, size, items, ...props }: AccordionProps<Value>): React.JSX.Element;
27
31
  export { Accordion };
28
32
  export type { AccordionItemProps, AccordionProps };
@@ -6,27 +6,52 @@ import theme from "../theme/accordion.mjs";
6
6
  import { PlusIcon } from "./icons/plus.mjs";
7
7
  function Accordion({
8
8
  className,
9
+ ui,
9
10
  size,
10
11
  items,
11
12
  ...props
12
13
  }) {
13
- const ui = theme({ size });
14
+ const styles = theme({ size });
14
15
  return /* @__PURE__ */ React.createElement(
15
16
  UIAccordion.Root,
16
17
  {
17
18
  ...props,
18
19
  "data-slot": "root",
19
- className: cn(ui.root(), className)
20
+ className: cn(styles.root(), ui?.root, className)
20
21
  },
21
- /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true", "data-slot": "glass", className: ui.glass() }, /* @__PURE__ */ React.createElement(
22
+ /* @__PURE__ */ React.createElement(
22
23
  "span",
23
24
  {
24
25
  "aria-hidden": "true",
25
- "data-slot": "overlay",
26
- className: ui.overlay()
26
+ "data-slot": "glass",
27
+ className: cn(styles.glass(), ui?.glass)
28
+ },
29
+ /* @__PURE__ */ React.createElement(
30
+ "span",
31
+ {
32
+ "aria-hidden": "true",
33
+ "data-slot": "overlay",
34
+ className: cn(styles.overlay(), ui?.overlay)
35
+ }
36
+ )
37
+ ),
38
+ /* @__PURE__ */ React.createElement("div", { "data-slot": "content", className: cn(styles.content(), ui?.content) }, items.map((item) => /* @__PURE__ */ React.createElement(
39
+ AccordionItem,
40
+ {
41
+ key: item.value,
42
+ ...item,
43
+ ui: {
44
+ item: cn(ui?.item, item.ui?.item),
45
+ header: cn(ui?.header, item.ui?.header),
46
+ trigger: cn(ui?.trigger, item.ui?.trigger),
47
+ title: cn(ui?.title, item.ui?.title),
48
+ indicator: cn(ui?.indicator, item.ui?.indicator),
49
+ indicatorIcon: cn(ui?.indicatorIcon, item.ui?.indicatorIcon),
50
+ panel: cn(ui?.panel, item.ui?.panel),
51
+ panelContent: cn(ui?.panelContent, item.ui?.panelContent)
52
+ }
27
53
  }
28
- )),
29
- /* @__PURE__ */ React.createElement("div", { "data-slot": "content", className: ui.content() }, items.map((item) => /* @__PURE__ */ React.createElement(AccordionItem, { key: item.value, ...item })))
54
+ )))
30
55
  );
31
56
  }
32
57
  function AccordionItem({
@@ -34,33 +59,66 @@ function AccordionItem({
34
59
  title,
35
60
  content,
36
61
  className,
62
+ ui,
63
+ indicator,
64
+ ref,
37
65
  ...props
38
66
  }) {
39
- const ui = theme();
67
+ const styles = theme();
40
68
  return /* @__PURE__ */ React.createElement(
41
69
  UIAccordion.Item,
42
70
  {
43
71
  ...props,
72
+ ref,
44
73
  value,
45
74
  "data-slot": "item",
46
- className: cn(ui.item(), className)
75
+ className: cn(styles.item(), ui?.item, className)
47
76
  },
48
- /* @__PURE__ */ React.createElement(UIAccordion.Header, { "data-slot": "header", className: ui.header() }, /* @__PURE__ */ React.createElement(UIAccordion.Trigger, { "data-slot": "trigger", className: ui.trigger() }, /* @__PURE__ */ React.createElement("span", { "data-slot": "title", className: ui.title() }, title), /* @__PURE__ */ React.createElement(
49
- "span",
77
+ /* @__PURE__ */ React.createElement(
78
+ UIAccordion.Header,
50
79
  {
51
- "aria-hidden": "true",
52
- "data-slot": "indicator",
53
- className: ui.indicator()
80
+ "data-slot": "header",
81
+ className: cn(styles.header(), ui?.header)
54
82
  },
55
83
  /* @__PURE__ */ React.createElement(
56
- PlusIcon,
84
+ UIAccordion.Trigger,
57
85
  {
58
- "data-slot": "indicator-icon",
59
- className: ui.indicatorIcon()
60
- }
86
+ "data-slot": "trigger",
87
+ className: cn(styles.trigger(), ui?.trigger)
88
+ },
89
+ /* @__PURE__ */ React.createElement("span", { "data-slot": "title", className: cn(styles.title(), ui?.title) }, title),
90
+ indicator === false ? null : /* @__PURE__ */ React.createElement(
91
+ "span",
92
+ {
93
+ "aria-hidden": "true",
94
+ "data-slot": "indicator",
95
+ className: cn(styles.indicator(), ui?.indicator)
96
+ },
97
+ indicator === void 0 ? /* @__PURE__ */ React.createElement(
98
+ PlusIcon,
99
+ {
100
+ "data-slot": "indicator-icon",
101
+ className: cn(styles.indicatorIcon(), ui?.indicatorIcon)
102
+ }
103
+ ) : indicator
104
+ )
105
+ )
106
+ ),
107
+ /* @__PURE__ */ React.createElement(
108
+ UIAccordion.Panel,
109
+ {
110
+ "data-slot": "panel",
111
+ className: cn(styles.panel(), ui?.panel)
112
+ },
113
+ /* @__PURE__ */ React.createElement(
114
+ "div",
115
+ {
116
+ "data-slot": "panel-content",
117
+ className: cn(styles.panelContent(), ui?.panelContent)
118
+ },
119
+ content
61
120
  )
62
- ))),
63
- /* @__PURE__ */ React.createElement(UIAccordion.Panel, { "data-slot": "panel", className: ui.panel() }, /* @__PURE__ */ React.createElement("div", { "data-slot": "panel-content", className: ui.panelContent() }, content))
121
+ )
64
122
  );
65
123
  }
66
124
  export { Accordion };
@@ -1,5 +1,8 @@
1
1
  import { AlertDialog as UIAlertDialog } from "@base-ui/react/alert-dialog";
2
2
  import * as React from "react";
3
+ import { type ComponentConfig } from "../internal/tv";
4
+ import theme from "../theme/alert-dialog";
5
+ type AlertDialog = ComponentConfig<typeof theme>;
3
6
  type AlertDialogProps = Omit<UIAlertDialog.Root.Props, "children"> & {
4
7
  /** Dialog heading. */
5
8
  title: React.ReactNode;
@@ -16,8 +19,10 @@ type AlertDialogProps = Omit<UIAlertDialog.Root.Props, "children"> & {
16
19
  /** Called when the confirm button is pressed. */
17
20
  onConfirm?: React.MouseEventHandler<HTMLButtonElement>;
18
21
  /** Tailwind classes merged into the popup. */
19
- className?: any;
22
+ className?: string;
23
+ /** Tailwind classes for internal slots, keyed by `data-slot`. */
24
+ ui?: AlertDialog["slots"];
20
25
  };
21
- declare function AlertDialog({ title, description, children, cancelLabel, confirmLabel, onCancel, onConfirm, className, ...props }: AlertDialogProps): React.JSX.Element;
26
+ declare function AlertDialog({ title, description, children, cancelLabel, confirmLabel, onCancel, onConfirm, className, ui, ...props }: AlertDialogProps): React.JSX.Element;
22
27
  export { AlertDialog };
23
28
  export type { AlertDialogProps };
@@ -13,49 +13,90 @@ function AlertDialog({
13
13
  onCancel,
14
14
  onConfirm,
15
15
  className,
16
+ ui,
16
17
  ...props
17
18
  }) {
18
- const ui = theme();
19
+ const styles = theme();
19
20
  return /* @__PURE__ */ React.createElement(UIAlertDialog.Root, { ...props }, /* @__PURE__ */ React.createElement(UIAlertDialog.Portal, null, /* @__PURE__ */ React.createElement(
20
21
  UIAlertDialog.Backdrop,
21
22
  {
22
23
  "data-slot": "backdrop",
23
- className: ui.backdrop()
24
+ className: cn(styles.backdrop(), ui?.backdrop)
24
25
  }
25
- ), /* @__PURE__ */ React.createElement(UIAlertDialog.Viewport, { "data-slot": "viewport", className: ui.viewport() }, /* @__PURE__ */ React.createElement(
26
- UIAlertDialog.Popup,
26
+ ), /* @__PURE__ */ React.createElement(
27
+ UIAlertDialog.Viewport,
27
28
  {
28
- "data-slot": "popup",
29
- className: cn(ui.popup(), className)
29
+ "data-slot": "viewport",
30
+ className: cn(styles.viewport(), ui?.viewport)
30
31
  },
31
- /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true", "data-slot": "glass", className: ui.glass() }, /* @__PURE__ */ React.createElement(
32
- "span",
32
+ /* @__PURE__ */ React.createElement(
33
+ UIAlertDialog.Popup,
33
34
  {
34
- "aria-hidden": "true",
35
- "data-slot": "overlay",
36
- className: ui.overlay()
37
- }
38
- )),
39
- /* @__PURE__ */ React.createElement("div", { "data-slot": "content", className: ui.content() }, /* @__PURE__ */ React.createElement(UIAlertDialog.Title, { "data-slot": "title", className: ui.title() }, title), description ? /* @__PURE__ */ React.createElement(
40
- UIAlertDialog.Description,
41
- {
42
- "data-slot": "description",
43
- className: ui.description()
35
+ "data-slot": "popup",
36
+ className: cn(styles.popup(), ui?.popup, className)
44
37
  },
45
- description
46
- ) : null, children ? /* @__PURE__ */ React.createElement("div", { "data-slot": "body", className: ui.body() }, children) : null, /* @__PURE__ */ React.createElement("div", { "data-slot": "footer", className: ui.footer() }, /* @__PURE__ */ React.createElement(
47
- UIAlertDialog.Close,
48
- {
49
- onClick: onCancel,
50
- render: /* @__PURE__ */ React.createElement(Button, { label: cancelLabel })
51
- }
52
- ), /* @__PURE__ */ React.createElement(
53
- UIAlertDialog.Close,
54
- {
55
- onClick: onConfirm,
56
- render: /* @__PURE__ */ React.createElement(Button, { label: confirmLabel })
57
- }
58
- )))
59
- ))));
38
+ /* @__PURE__ */ React.createElement(
39
+ "span",
40
+ {
41
+ "aria-hidden": "true",
42
+ "data-slot": "glass",
43
+ className: cn(styles.glass(), ui?.glass)
44
+ },
45
+ /* @__PURE__ */ React.createElement(
46
+ "span",
47
+ {
48
+ "aria-hidden": "true",
49
+ "data-slot": "overlay",
50
+ className: cn(styles.overlay(), ui?.overlay)
51
+ }
52
+ )
53
+ ),
54
+ /* @__PURE__ */ React.createElement(
55
+ "div",
56
+ {
57
+ "data-slot": "content",
58
+ className: cn(styles.content(), ui?.content)
59
+ },
60
+ /* @__PURE__ */ React.createElement(
61
+ UIAlertDialog.Title,
62
+ {
63
+ "data-slot": "title",
64
+ className: cn(styles.title(), ui?.title)
65
+ },
66
+ title
67
+ ),
68
+ description ? /* @__PURE__ */ React.createElement(
69
+ UIAlertDialog.Description,
70
+ {
71
+ "data-slot": "description",
72
+ className: cn(styles.description(), ui?.description)
73
+ },
74
+ description
75
+ ) : null,
76
+ children ? /* @__PURE__ */ React.createElement("div", { "data-slot": "body", className: cn(styles.body(), ui?.body) }, children) : null,
77
+ /* @__PURE__ */ React.createElement(
78
+ "div",
79
+ {
80
+ "data-slot": "footer",
81
+ className: cn(styles.footer(), ui?.footer)
82
+ },
83
+ /* @__PURE__ */ React.createElement(
84
+ UIAlertDialog.Close,
85
+ {
86
+ onClick: onCancel,
87
+ render: /* @__PURE__ */ React.createElement(Button, { label: cancelLabel })
88
+ }
89
+ ),
90
+ /* @__PURE__ */ React.createElement(
91
+ UIAlertDialog.Close,
92
+ {
93
+ onClick: onConfirm,
94
+ render: /* @__PURE__ */ React.createElement(Button, { label: confirmLabel })
95
+ }
96
+ )
97
+ )
98
+ )
99
+ )
100
+ )));
60
101
  }
61
102
  export { AlertDialog };
@@ -1,12 +1,16 @@
1
1
  import { type ButtonProps as UIButtonProps } from "@base-ui/react/button";
2
2
  import * as React from "react";
3
3
  import { type VariantProps } from "tailwind-variants";
4
+ import { type ComponentConfig } from "../internal/tv";
4
5
  import theme from "../theme/button";
6
+ type Button = ComponentConfig<typeof theme>;
5
7
  interface ButtonProps extends Omit<UIButtonProps, "ref" | "style" | "className">, VariantProps<typeof theme> {
6
8
  /** Ref to the rendered root element. */
7
9
  ref?: React.Ref<HTMLElement>;
8
10
  /** Tailwind classes merged into the outer click area. */
9
- className?: any;
11
+ className?: string;
12
+ /** Tailwind classes for internal slots, keyed by `data-slot`. */
13
+ ui?: Button["slots"];
10
14
  /** Text or content rendered inside the button. */
11
15
  label?: React.ReactNode;
12
16
  /** Icon rendered before the label. */
@@ -14,6 +18,6 @@ interface ButtonProps extends Omit<UIButtonProps, "ref" | "style" | "className">
14
18
  /** Icon rendered after the label. */
15
19
  trailingIcon?: React.ReactNode;
16
20
  }
17
- declare function Button({ className, children, label, leadingIcon, trailingIcon, size, ...props }: ButtonProps): React.JSX.Element;
21
+ declare function Button({ className, ui, children, label, leadingIcon, trailingIcon, size, ...props }: ButtonProps): React.JSX.Element;
18
22
  export { Button };
19
23
  export type { ButtonProps };
@@ -7,6 +7,7 @@ import * as React from "react";
7
7
  import theme from "../theme/button.mjs";
8
8
  function Button({
9
9
  className,
10
+ ui,
10
11
  children,
11
12
  label,
12
13
  leadingIcon,
@@ -14,34 +15,42 @@ function Button({
14
15
  size,
15
16
  ...props
16
17
  }) {
17
- const ui = theme({ size });
18
+ const styles = theme({ size });
18
19
  return /* @__PURE__ */ React.createElement(
19
20
  UIButton,
20
21
  {
21
22
  "data-slot": "root",
22
- className: cn(ui.root(), className),
23
+ className: cn(styles.root(), ui?.root, className),
23
24
  ...props
24
25
  },
25
- /* @__PURE__ */ React.createElement("span", { "data-slot": "base", className: ui.base() }, /* @__PURE__ */ React.createElement("span", { "data-slot": "glass", "aria-hidden": "true", className: ui.glass() }, /* @__PURE__ */ React.createElement(
26
+ /* @__PURE__ */ React.createElement("span", { "data-slot": "base", className: cn(styles.base(), ui?.base) }, /* @__PURE__ */ React.createElement(
26
27
  "span",
27
28
  {
28
- "data-slot": "overlay",
29
- className: ui.overlay(),
30
- "aria-hidden": "true"
31
- }
32
- )), leadingIcon ? /* @__PURE__ */ React.createElement(
29
+ "data-slot": "glass",
30
+ "aria-hidden": "true",
31
+ className: cn(styles.glass(), ui?.glass)
32
+ },
33
+ /* @__PURE__ */ React.createElement(
34
+ "span",
35
+ {
36
+ "data-slot": "overlay",
37
+ className: cn(styles.overlay(), ui?.overlay),
38
+ "aria-hidden": "true"
39
+ }
40
+ )
41
+ ), leadingIcon ? /* @__PURE__ */ React.createElement(
33
42
  "span",
34
43
  {
35
44
  "data-slot": "leading-icon",
36
- className: ui.leadingIcon(),
45
+ className: cn(styles.leadingIcon(), ui?.leadingIcon),
37
46
  "aria-hidden": "true"
38
47
  },
39
48
  leadingIcon
40
- ) : null, /* @__PURE__ */ React.createElement("span", { "data-slot": "label", className: ui.label() }, label || children), trailingIcon ? /* @__PURE__ */ React.createElement(
49
+ ) : null, /* @__PURE__ */ React.createElement("span", { "data-slot": "label", className: cn(styles.label(), ui?.label) }, label || children), trailingIcon ? /* @__PURE__ */ React.createElement(
41
50
  "span",
42
51
  {
43
52
  "data-slot": "trailing-icon",
44
- className: ui.trailingIcon(),
53
+ className: cn(styles.trailingIcon(), ui?.trailingIcon),
45
54
  "aria-hidden": "true"
46
55
  },
47
56
  trailingIcon
@@ -1,7 +1,12 @@
1
1
  import * as React from "react";
2
+ import { type ComponentConfig } from "../internal/tv";
3
+ import theme from "../theme/card";
4
+ type Card = ComponentConfig<typeof theme>;
2
5
  type CardProps = Omit<React.ComponentProps<"div">, "title"> & {
3
6
  /** Classes merged into the root element. */
4
7
  className?: string;
8
+ /** Tailwind classes for internal slots, keyed by `data-slot`. */
9
+ ui?: Card["slots"];
5
10
  /** Optional visible title rendered inside the card header. */
6
11
  title?: React.ReactNode;
7
12
  /** Optional supporting copy rendered under the title. */
@@ -9,6 +14,6 @@ type CardProps = Omit<React.ComponentProps<"div">, "title"> & {
9
14
  /** Optional footer row rendered below the body. */
10
15
  footer?: React.ReactNode;
11
16
  };
12
- declare function Card({ className, title, description, footer, children, ...props }: CardProps): React.JSX.Element;
17
+ declare function Card({ className, ui, title, description, footer, children, ...props }: CardProps): React.JSX.Element;
13
18
  export { Card };
14
19
  export type { CardProps };
@@ -3,22 +3,45 @@ import * as React from "react";
3
3
  import theme from "../theme/card.mjs";
4
4
  function Card({
5
5
  className,
6
+ ui,
6
7
  title,
7
8
  description,
8
9
  footer,
9
10
  children,
10
11
  ...props
11
12
  }) {
12
- const ui = theme();
13
+ const styles = theme();
13
14
  return /* @__PURE__ */ React.createElement(
14
15
  "div",
15
16
  {
16
17
  "data-slot": "root",
17
- className: cn(ui.root(), className),
18
+ className: cn(styles.root(), ui?.root, className),
18
19
  ...props
19
20
  },
20
- /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", className: ui.glass() }, /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true", className: ui.overlay() })),
21
- /* @__PURE__ */ React.createElement("div", { className: ui.layout() }, title || description ? /* @__PURE__ */ React.createElement("div", { "data-slot": "header", className: ui.header() }, title ? /* @__PURE__ */ React.createElement("div", { "data-slot": "title", className: ui.title() }, title) : null, description ? /* @__PURE__ */ React.createElement("div", { "data-slot": "description", className: ui.description() }, description) : null) : null, children ? /* @__PURE__ */ React.createElement("div", { "data-slot": "body", className: ui.body() }, children) : null, footer ? /* @__PURE__ */ React.createElement("div", { "data-slot": "footer", className: ui.footer() }, footer) : null)
21
+ /* @__PURE__ */ React.createElement(
22
+ "div",
23
+ {
24
+ "aria-hidden": "true",
25
+ "data-slot": "glass",
26
+ className: cn(styles.glass(), ui?.glass)
27
+ },
28
+ /* @__PURE__ */ React.createElement(
29
+ "span",
30
+ {
31
+ "aria-hidden": "true",
32
+ "data-slot": "overlay",
33
+ className: cn(styles.overlay(), ui?.overlay)
34
+ }
35
+ )
36
+ ),
37
+ /* @__PURE__ */ React.createElement("div", { "data-slot": "layout", className: cn(styles.layout(), ui?.layout) }, title || description ? /* @__PURE__ */ React.createElement("div", { "data-slot": "header", className: cn(styles.header(), ui?.header) }, title ? /* @__PURE__ */ React.createElement("div", { "data-slot": "title", className: cn(styles.title(), ui?.title) }, title) : null, description ? /* @__PURE__ */ React.createElement(
38
+ "div",
39
+ {
40
+ "data-slot": "description",
41
+ className: cn(styles.description(), ui?.description)
42
+ },
43
+ description
44
+ ) : null) : null, children ? /* @__PURE__ */ React.createElement("div", { "data-slot": "body", className: cn(styles.body(), ui?.body) }, children) : null, footer ? /* @__PURE__ */ React.createElement("div", { "data-slot": "footer", className: cn(styles.footer(), ui?.footer) }, footer) : null)
22
45
  );
23
46
  }
24
47
  export { Card };
@@ -1,17 +1,21 @@
1
1
  import { Checkbox as UICheckbox } from "@base-ui/react/checkbox";
2
2
  import * as React from "react";
3
3
  import { type VariantProps } from "tailwind-variants";
4
+ import { type ComponentConfig } from "../internal/tv";
4
5
  import theme from "../theme/checkbox";
6
+ type Checkbox = ComponentConfig<typeof theme>;
5
7
  interface CheckboxProps extends Omit<UICheckbox.Root.Props, "className" | "children" | "style" | "ref">, VariantProps<typeof theme> {
6
8
  /** Ref to the rendered root element. */
7
9
  ref?: React.Ref<HTMLElement>;
8
10
  /** Tailwind classes merged into the Base UI checkbox root. */
9
11
  className?: string;
12
+ /** Tailwind classes for internal slots, keyed by `data-slot`. */
13
+ ui?: Checkbox["slots"];
10
14
  /** Visible label. */
11
15
  label?: React.ReactNode;
12
16
  /** Optional supporting text. */
13
17
  description?: React.ReactNode;
14
18
  }
15
- declare function Checkbox({ className, label, description, size, indeterminate, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...props }: CheckboxProps): React.JSX.Element;
19
+ declare function Checkbox({ className, ui, label, description, size, indeterminate, "aria-describedby": ariaDescribedBy, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, ...props }: CheckboxProps): React.JSX.Element;
16
20
  export { Checkbox };
17
21
  export type { CheckboxProps };