@ponti-studios/ui 0.1.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -7
  3. package/THIRD_PARTY_NOTICES.md +17 -0
  4. package/dist/index.js +988 -640
  5. package/dist/index.js.map +1 -1
  6. package/package.json +41 -41
  7. package/src/animations.css +42 -383
  8. package/src/components/data-display/accordion.tsx +22 -10
  9. package/src/components/data-display/filter-chip.stories.tsx +38 -0
  10. package/src/components/data-display/filter-chip.tsx +10 -4
  11. package/src/components/data-display/index.ts +5 -0
  12. package/src/components/data-display/metric-card.stories.tsx +48 -0
  13. package/src/components/data-display/metric-card.tsx +20 -0
  14. package/src/components/data-display/sort-controls.stories.tsx +45 -0
  15. package/src/components/data-display/sort-controls.tsx +99 -0
  16. package/src/components/data-display/sort-row.tsx +74 -0
  17. package/src/components/data-display/table.tsx +5 -9
  18. package/src/components/feedback/alert.tsx +4 -4
  19. package/src/components/feedback/empty-state.tsx +16 -6
  20. package/src/components/feedback/file-upload-status-badge.stories.tsx +28 -0
  21. package/src/components/feedback/file-upload-status-badge.tsx +33 -0
  22. package/src/components/feedback/file-upload-status.stories.tsx +57 -0
  23. package/src/components/feedback/file-upload-status.tsx +152 -0
  24. package/src/components/feedback/index.ts +9 -0
  25. package/src/components/feedback/progress-bar.stories.tsx +39 -0
  26. package/src/components/feedback/progress-bar.tsx +21 -0
  27. package/src/components/feedback/progress.stories.tsx +8 -6
  28. package/src/components/feedback/progress.tsx +3 -3
  29. package/src/components/feedback/skeleton.stories.tsx +36 -0
  30. package/src/components/feedback/skeleton.tsx +7 -0
  31. package/src/components/feedback/spinner.tsx +2 -2
  32. package/src/components/feedback/update-guard.stories.tsx +32 -0
  33. package/src/components/feedback/update-guard.tsx +280 -0
  34. package/src/components/forms/calendar.tsx +14 -11
  35. package/src/components/forms/date-month-select.stories.tsx +26 -0
  36. package/src/components/forms/date-month-select.tsx +70 -0
  37. package/src/components/forms/date-picker.tsx +3 -3
  38. package/src/components/forms/drop-zone.stories.tsx +56 -0
  39. package/src/components/forms/drop-zone.tsx +146 -0
  40. package/src/components/forms/entity-select.stories.tsx +65 -0
  41. package/src/components/forms/entity-select.tsx +79 -0
  42. package/src/components/forms/field.tsx +3 -3
  43. package/src/components/forms/group-by-select.stories.tsx +26 -0
  44. package/src/components/forms/group-by-select.tsx +47 -0
  45. package/src/components/forms/index.ts +10 -0
  46. package/src/components/forms/input.stories.tsx +4 -4
  47. package/src/components/forms/input.tsx +1 -1
  48. package/src/components/forms/passkey-management.stories.tsx +81 -0
  49. package/src/components/forms/passkey-management.tsx +138 -0
  50. package/src/components/forms/radio-group.tsx +14 -13
  51. package/src/components/forms/select.stories.tsx +4 -4
  52. package/src/components/forms/select.tsx +47 -48
  53. package/src/components/forms/slider.stories.tsx +5 -2
  54. package/src/components/forms/slider.tsx +39 -17
  55. package/src/components/forms/stepper.tsx +4 -4
  56. package/src/components/forms/switch.stories.tsx +1 -1
  57. package/src/components/forms/switch.tsx +2 -2
  58. package/src/components/forms/textarea.stories.tsx +2 -2
  59. package/src/components/forms/textarea.tsx +1 -1
  60. package/src/components/layout/index.ts +3 -0
  61. package/src/components/layout/scroll-area.stories.tsx +4 -4
  62. package/src/components/layout/scroll-area.tsx +23 -21
  63. package/src/components/layout/section-intro.stories.tsx +29 -0
  64. package/src/components/layout/section-intro.tsx +31 -0
  65. package/src/components/layout/surface-panel.stories.tsx +32 -0
  66. package/src/components/layout/surface-panel.tsx +15 -0
  67. package/src/components/navigation/app-navigation.stories.tsx +2 -2
  68. package/src/components/navigation/app-navigation.tsx +13 -13
  69. package/src/components/navigation/index.ts +4 -0
  70. package/src/components/navigation/pagination-controls.stories.tsx +41 -0
  71. package/src/components/navigation/pagination-controls.tsx +50 -0
  72. package/src/components/navigation/route-link.stories.tsx +30 -0
  73. package/src/components/navigation/route-link.tsx +42 -0
  74. package/src/components/navigation/tabs.stories.tsx +5 -5
  75. package/src/components/navigation/tabs.tsx +8 -8
  76. package/src/components/overlays/alert-dialog.tsx +22 -25
  77. package/src/components/overlays/command.tsx +98 -26
  78. package/src/components/overlays/dialog.tsx +5 -5
  79. package/src/components/overlays/dropdown-menu.stories.tsx +0 -5
  80. package/src/components/overlays/dropdown-menu.tsx +49 -31
  81. package/src/components/overlays/popover.stories.tsx +2 -2
  82. package/src/components/overlays/popover.tsx +43 -14
  83. package/src/components/overlays/sheet.stories.tsx +2 -2
  84. package/src/components/overlays/sheet.tsx +51 -17
  85. package/src/components/primitives/avatar.tsx +9 -5
  86. package/src/components/primitives/badge.tsx +11 -12
  87. package/src/components/primitives/button.tsx +49 -23
  88. package/src/components/primitives/card.stories.tsx +2 -2
  89. package/src/components/primitives/card.tsx +2 -5
  90. package/src/components/primitives/index.ts +2 -0
  91. package/src/components/primitives/label.stories.tsx +1 -1
  92. package/src/components/primitives/label.tsx +5 -6
  93. package/src/components/primitives/status-badge.stories.tsx +52 -0
  94. package/src/components/primitives/status-badge.tsx +33 -0
  95. package/src/constants/chart-colors.ts +7 -7
  96. package/src/hooks/index.ts +1 -0
  97. package/src/hooks/use-sort.ts +20 -0
  98. package/src/index.ts +30 -11
  99. package/src/styles.css +100 -974
  100. package/src/tokens/chart-colors.stories.tsx +192 -0
  101. package/src/tokens/color-systems.stories.tsx +20 -112
  102. package/src/tokens/foundations.stories.tsx +58 -100
  103. package/src/tokens/generated/dark.colors.generated.ts +29 -0
  104. package/src/tokens/generated/dark.css +29 -0
  105. package/src/tokens/generated/foundations.css +148 -0
  106. package/src/tokens/generated/foundations.generated.ts +451 -0
  107. package/src/tokens/generated/light.colors.generated.ts +29 -0
  108. package/src/tokens/generated/light.css +29 -0
  109. package/src/tokens/index.ts +79 -16
  110. package/src/tokens/layout.stories.tsx +23 -91
  111. package/src/tokens/colors.ts +0 -156
  112. package/src/tokens/motion.ts +0 -51
  113. package/src/tokens/radii.ts +0 -10
  114. package/src/tokens/shadows.ts +0 -34
  115. package/src/tokens/spacing.ts +0 -30
  116. package/src/tokens/typography.ts +0 -42
@@ -1,4 +1,4 @@
1
- import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
1
+ import { AlertDialog as AlertDialogPrimitive } from "@base-ui/react/alert-dialog";
2
2
  import * as React from "react";
3
3
 
4
4
  import { cn } from "../../lib/utils";
@@ -7,47 +7,47 @@ import { buttonVariants } from "../primitives/button";
7
7
  const AlertDialog = AlertDialogPrimitive.Root;
8
8
  const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
9
9
 
10
- const AlertDialogPortal = ({ children, ...props }: AlertDialogPrimitive.AlertDialogPortalProps) => (
10
+ const AlertDialogPortal = ({
11
+ children,
12
+ ...props
13
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) => (
11
14
  <AlertDialogPrimitive.Portal {...props}>
12
15
  <div className="fixed inset-0 z-50 flex items-end justify-center sm:items-center">
13
16
  {children}
14
17
  </div>
15
18
  </AlertDialogPrimitive.Portal>
16
19
  );
17
- AlertDialogPortal.displayName = AlertDialogPrimitive.Portal.displayName;
18
20
 
19
21
  const AlertDialogOverlay = React.forwardRef<
20
- React.ComponentRef<typeof AlertDialogPrimitive.Overlay>,
21
- React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>
22
+ React.ComponentRef<typeof AlertDialogPrimitive.Backdrop>,
23
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Backdrop>
22
24
  >(({ className, ...props }, ref) => (
23
- <AlertDialogPrimitive.Overlay
25
+ <AlertDialogPrimitive.Backdrop
24
26
  ref={ref}
25
27
  className={cn(
26
- "overlay-backdrop data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 transition-opacity",
28
+ "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 bg-black/80 transition-opacity",
27
29
  className,
28
30
  )}
29
31
  {...props}
30
32
  />
31
33
  ));
32
- AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
33
34
 
34
35
  const AlertDialogContent = React.forwardRef<
35
- React.ComponentRef<typeof AlertDialogPrimitive.Content>,
36
- React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content>
36
+ React.ComponentRef<typeof AlertDialogPrimitive.Popup>,
37
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Popup>
37
38
  >(({ className, ...props }, ref) => (
38
39
  <AlertDialogPortal>
39
40
  <AlertDialogOverlay />
40
- <AlertDialogPrimitive.Content
41
+ <AlertDialogPrimitive.Popup
41
42
  ref={ref}
42
43
  className={cn(
43
- "bg-raised text-primary data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=open]:slide-in-from-bottom-10 data-[state=closed]:slide-out-to-bottom-10 border-subtle sm:data-[state=open]:slide-in-from-bottom-0 fixed bottom-0 z-50 grid w-full max-w-lg gap-6 rounded-t-xl border p-4 outline-none sm:top-[50%] sm:right-auto sm:bottom-auto sm:left-[50%] sm:-translate-x-1/2 sm:-translate-y-1/2 sm:rounded-xl sm:p-6",
44
+ "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 sm:data-open:slide-in-from-bottom-0 fixed bottom-0 z-50 grid w-full max-w-lg gap-6 rounded-t-xl border p-4 outline-none sm:top-[50%] sm:right-auto sm:bottom-auto sm:left-[50%] sm:-translate-x-1/2 sm:-translate-y-1/2 sm:rounded-xl sm:p-6",
44
45
  className,
45
46
  )}
46
47
  {...props}
47
48
  />
48
49
  </AlertDialogPortal>
49
50
  ));
50
- AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
51
51
 
52
52
  function AlertDialogHeader({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
53
53
  return <div className={cn("flex flex-col gap-2 text-left", className)} {...props} />;
@@ -68,11 +68,10 @@ const AlertDialogTitle = React.forwardRef<
68
68
  >(({ className, ...props }, ref) => (
69
69
  <AlertDialogPrimitive.Title
70
70
  ref={ref}
71
- className={cn("heading-2 text-primary", className)}
71
+ className={cn("text-lg font-semibold tracking-tight", className)}
72
72
  {...props}
73
73
  />
74
74
  ));
75
- AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
76
75
 
77
76
  const AlertDialogDescription = React.forwardRef<
78
77
  React.ComponentRef<typeof AlertDialogPrimitive.Description>,
@@ -80,31 +79,28 @@ const AlertDialogDescription = React.forwardRef<
80
79
  >(({ className, ...props }, ref) => (
81
80
  <AlertDialogPrimitive.Description
82
81
  ref={ref}
83
- className={cn("body-2 text-secondary max-w-prose", className)}
82
+ className={cn("text-muted-foreground max-w-prose text-sm", className)}
84
83
  {...props}
85
84
  />
86
85
  ));
87
- AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
88
86
 
89
87
  const AlertDialogAction = React.forwardRef<
90
- React.ComponentRef<typeof AlertDialogPrimitive.Action>,
91
- React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Action>
88
+ React.ComponentRef<typeof AlertDialogPrimitive.Close>,
89
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Close>
92
90
  >(({ className, ...props }, ref) => (
93
- <AlertDialogPrimitive.Action ref={ref} className={cn(buttonVariants(), className)} {...props} />
91
+ <AlertDialogPrimitive.Close ref={ref} className={cn(buttonVariants(), className)} {...props} />
94
92
  ));
95
- AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
96
93
 
97
94
  const AlertDialogCancel = React.forwardRef<
98
- React.ComponentRef<typeof AlertDialogPrimitive.Cancel>,
99
- React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Cancel>
95
+ React.ComponentRef<typeof AlertDialogPrimitive.Close>,
96
+ React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Close>
100
97
  >(({ className, ...props }, ref) => (
101
- <AlertDialogPrimitive.Cancel
98
+ <AlertDialogPrimitive.Close
102
99
  ref={ref}
103
100
  className={cn(buttonVariants({ variant: "outline" }), "mt-2 sm:mt-0", className)}
104
101
  {...props}
105
102
  />
106
103
  ));
107
- AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
108
104
 
109
105
  export {
110
106
  AlertDialog,
@@ -114,6 +110,7 @@ export {
114
110
  AlertDialogDescription,
115
111
  AlertDialogFooter,
116
112
  AlertDialogHeader,
113
+ AlertDialogOverlay,
117
114
  AlertDialogTitle,
118
115
  AlertDialogTrigger,
119
116
  };
@@ -1,16 +1,23 @@
1
- import { Command as CommandPrimitive } from "cmdk";
1
+ import { Combobox as CommandPrimitive } from "@base-ui/react/combobox";
2
2
  import { SearchIcon } from "lucide-react";
3
3
  import * as React from "react";
4
4
 
5
5
  import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./dialog";
6
6
 
7
- function Command(props: React.ComponentProps<typeof CommandPrimitive>) {
7
+ function Command({
8
+ className,
9
+ children,
10
+ ...props
11
+ }: Omit<React.ComponentProps<typeof CommandPrimitive.Root<string>>, "className"> & {
12
+ className?: string;
13
+ }) {
8
14
  return (
9
- <CommandPrimitive
15
+ <div
10
16
  data-slot="command"
11
- className="bg-raised text-primary flex h-full w-full flex-col overflow-hidden rounded-md"
12
- {...props}
13
- />
17
+ className={`bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md ${className ?? ""}`}
18
+ >
19
+ <CommandPrimitive.Root {...props}>{children}</CommandPrimitive.Root>
20
+ </div>
14
21
  );
15
22
  }
16
23
 
@@ -39,26 +46,70 @@ function CommandDialog({
39
46
  );
40
47
  }
41
48
 
42
- function CommandInput(props: React.ComponentProps<typeof CommandPrimitive.Input>) {
49
+ type CommandInputProps = React.ComponentProps<typeof CommandPrimitive.Input> & {
50
+ onValueChange?: (value: string) => void;
51
+ };
52
+
53
+ function CommandInput({
54
+ className,
55
+ onValueChange,
56
+ onChange,
57
+ "aria-label": ariaLabel = "Search commands",
58
+ ...props
59
+ }: CommandInputProps) {
43
60
  return (
44
61
  <div data-slot="command-input-wrapper" className="flex h-9 items-center gap-2 border-b px-3">
45
62
  <SearchIcon className="size-4 shrink-0 opacity-50" />
46
63
  <CommandPrimitive.Input
47
64
  data-slot="command-input"
48
- className="placeholder:text-secondary flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50"
65
+ aria-label={ariaLabel}
66
+ className={`placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50 ${className ?? ""}`}
67
+ onChange={(event) => {
68
+ onChange?.(event);
69
+ onValueChange?.(event.currentTarget.value);
70
+ }}
49
71
  {...props}
50
72
  />
51
73
  </div>
52
74
  );
53
75
  }
54
76
 
55
- function CommandList(props: React.ComponentProps<typeof CommandPrimitive.List>) {
77
+ function CommandList({ children, ...props }: React.ComponentProps<typeof CommandPrimitive.List>) {
78
+ if (typeof children === "function") {
79
+ return (
80
+ <CommandPrimitive.List
81
+ data-slot="command-list"
82
+ aria-label="Command results"
83
+ tabIndex={0}
84
+ className="max-h-75 scroll-py-1 overflow-x-hidden overflow-y-auto"
85
+ {...props}
86
+ >
87
+ {children}
88
+ </CommandPrimitive.List>
89
+ );
90
+ }
91
+
92
+ const listChildren: React.ReactNode[] = [];
93
+ const emptyChildren: React.ReactNode[] = [];
94
+
95
+ React.Children.forEach(children, (child) => {
96
+ if (React.isValidElement(child) && child.type === CommandEmpty) emptyChildren.push(child);
97
+ else listChildren.push(child);
98
+ });
99
+
56
100
  return (
57
- <CommandPrimitive.List
58
- data-slot="command-list"
59
- className="max-h-75 scroll-py-1 overflow-x-hidden overflow-y-auto"
60
- {...props}
61
- />
101
+ <div data-slot="command-list-container" className="relative">
102
+ <CommandPrimitive.List
103
+ data-slot="command-list"
104
+ aria-label="Command results"
105
+ tabIndex={0}
106
+ className="max-h-75 scroll-py-1 overflow-x-hidden overflow-y-auto"
107
+ {...props}
108
+ >
109
+ {listChildren}
110
+ </CommandPrimitive.List>
111
+ {emptyChildren}
112
+ </div>
62
113
  );
63
114
  }
64
115
 
@@ -72,31 +123,52 @@ function CommandEmpty({ ...props }: React.ComponentProps<typeof CommandPrimitive
72
123
  );
73
124
  }
74
125
 
75
- function CommandGroup(props: React.ComponentProps<typeof CommandPrimitive.Group>) {
126
+ function CommandGroup({
127
+ className,
128
+ heading,
129
+ children,
130
+ ...props
131
+ }: React.ComponentProps<typeof CommandPrimitive.Group> & { heading?: React.ReactNode }) {
76
132
  return (
77
133
  <CommandPrimitive.Group
78
134
  data-slot="command-group"
79
- className="text-primary **:[[cmdk-group-heading]]:text-secondary overflow-hidden p-1 **:[[cmdk-group-heading]]:px-2 **:[[cmdk-group-heading]]:py-1.5 **:[[cmdk-group-heading]]:text-xs **:[[cmdk-group-heading]]:font-medium"
135
+ className={`text-foreground overflow-hidden p-1 ${className ?? ""}`}
80
136
  {...props}
81
- />
137
+ >
138
+ {heading ? (
139
+ <CommandPrimitive.GroupLabel className="text-muted-foreground px-2 py-1.5 text-xs font-medium">
140
+ {heading}
141
+ </CommandPrimitive.GroupLabel>
142
+ ) : null}
143
+ {children}
144
+ </CommandPrimitive.Group>
82
145
  );
83
146
  }
84
147
 
85
- function CommandSeparator(props: React.ComponentProps<typeof CommandPrimitive.Separator>) {
148
+ type CommandItemProps = React.ComponentProps<typeof CommandPrimitive.Item> & {
149
+ onSelect?: () => void;
150
+ };
151
+
152
+ function CommandItem({ className, onSelect, onClick, ...props }: CommandItemProps) {
86
153
  return (
87
- <CommandPrimitive.Separator
88
- data-slot="command-separator"
89
- className="bg-border -mx-1 h-px"
154
+ <CommandPrimitive.Item
155
+ data-slot="command-item"
156
+ className={`data-highlighted:bg-muted data-highlighted:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 ${className ?? ""}`}
157
+ onClick={(event) => {
158
+ onClick?.(event);
159
+ onSelect?.();
160
+ }}
90
161
  {...props}
91
162
  />
92
163
  );
93
164
  }
94
165
 
95
- function CommandItem(props: React.ComponentProps<typeof CommandPrimitive.Item>) {
166
+ function CommandSeparator({ className, ...props }: React.ComponentProps<"div">) {
96
167
  return (
97
- <CommandPrimitive.Item
98
- data-slot="command-item"
99
- className="data-[selected=true]:bg-accent data-[selected=true]:text-on-accent [&_svg:not([class*='text-'])]:text-secondary relative flex cursor-default items-center gap-2 rounded-md px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4"
168
+ <div
169
+ data-slot="command-separator"
170
+ aria-hidden="true"
171
+ className={`bg-border -mx-1 h-px ${className ?? ""}`}
100
172
  {...props}
101
173
  />
102
174
  );
@@ -106,7 +178,7 @@ function CommandShortcut(props: React.ComponentProps<"span">) {
106
178
  return (
107
179
  <span
108
180
  data-slot="command-shortcut"
109
- className="text-secondary ml-auto text-xs tracking-widest"
181
+ className="text-muted-foreground ml-auto text-xs tracking-widest"
110
182
  {...props}
111
183
  />
112
184
  );
@@ -63,7 +63,7 @@ function DialogOverlay({ className, ...props }: React.ComponentProps<typeof Base
63
63
  <BaseDialog.Backdrop
64
64
  data-slot="dialog-overlay"
65
65
  className={cn(
66
- "overlay-backdrop data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 transition-opacity",
66
+ "data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 fixed inset-0 z-50 bg-black/80 transition-opacity",
67
67
  className,
68
68
  )}
69
69
  {...props}
@@ -85,7 +85,7 @@ function DialogContent({
85
85
  <BaseDialog.Popup
86
86
  data-slot="dialog-content"
87
87
  className={cn(
88
- "bg-raised text-primary border-subtle data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid max-h-[90dvh] w-[calc(100%-2rem)] max-w-[30rem] translate-x-[-50%] translate-y-[-50%] gap-6 overflow-y-auto rounded-xl border p-4 duration-200 outline-none sm:p-6",
88
+ "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid max-h-[90dvh] w-[calc(100%-2rem)] max-w-[30rem] translate-x-[-50%] translate-y-[-50%] gap-6 overflow-y-auto rounded-lg border p-4 duration-200 outline-none sm:p-6",
89
89
  className,
90
90
  )}
91
91
  {...props}
@@ -94,7 +94,7 @@ function DialogContent({
94
94
  {showCloseButton && (
95
95
  <BaseDialog.Close
96
96
  data-slot="dialog-close"
97
- className="text-secondary hover:bg-inset hover:text-primary focus-visible:outline-ring absolute top-3 right-3 inline-flex size-11 items-center justify-center rounded-md border border-transparent opacity-100 transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 disabled:pointer-events-none sm:top-4 sm:right-4 [&_svg]:pointer-events-none [&_svg]:size-4.5 [&_svg]:shrink-0"
97
+ className="text-muted-foreground hover:bg-muted hover:text-muted-foreground focus-visible:outline-ring absolute top-3 right-3 inline-flex size-9 items-center justify-center rounded-md border border-transparent opacity-100 transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 disabled:pointer-events-none sm:top-4 sm:right-4 [&_svg]:pointer-events-none [&_svg]:size-4.5 [&_svg]:shrink-0"
98
98
  >
99
99
  <XIcon />
100
100
  <span className="sr-only">Close</span>
@@ -129,7 +129,7 @@ function DialogTitle({ className, ...props }: React.ComponentProps<typeof BaseDi
129
129
  return (
130
130
  <BaseDialog.Title
131
131
  data-slot="dialog-title"
132
- className={cn("heading-2 text-primary", className)}
132
+ className={cn("text-lg leading-none font-semibold tracking-tight", className)}
133
133
  {...props}
134
134
  />
135
135
  );
@@ -142,7 +142,7 @@ function DialogDescription({
142
142
  return (
143
143
  <BaseDialog.Description
144
144
  data-slot="dialog-description"
145
- className={cn("body-2 text-secondary max-w-prose", className)}
145
+ className={cn("text-muted-foreground max-w-prose text-sm", className)}
146
146
  {...props}
147
147
  />
148
148
  );
@@ -31,11 +31,6 @@ const meta = {
31
31
  description: "When true, interaction outside the menu is disabled while open",
32
32
  table: { defaultValue: { summary: "true" } },
33
33
  },
34
- dir: {
35
- control: "select",
36
- options: ["ltr", "rtl"],
37
- description: "Reading direction of the dropdown menu",
38
- },
39
34
  },
40
35
  } satisfies Meta<typeof DropdownMenu>;
41
36
 
@@ -1,4 +1,4 @@
1
- import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
1
+ import { Menu as DropdownMenuPrimitive } from "@base-ui/react/menu";
2
2
  import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react";
3
3
  import * as React from "react";
4
4
 
@@ -12,26 +12,44 @@ function DropdownMenuPortal(props: React.ComponentProps<typeof DropdownMenuPrimi
12
12
  return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
13
13
  }
14
14
 
15
- function DropdownMenuTrigger(props: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
16
- return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;
15
+ function DropdownMenuTrigger({
16
+ asChild,
17
+ children,
18
+ ...props
19
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger> & { asChild?: boolean }) {
20
+ return (
21
+ <DropdownMenuPrimitive.Trigger
22
+ data-slot="dropdown-menu-trigger"
23
+ {...(asChild && React.isValidElement(children)
24
+ ? { render: children as React.ReactElement }
25
+ : {})}
26
+ {...props}
27
+ >
28
+ {!asChild && children}
29
+ </DropdownMenuPrimitive.Trigger>
30
+ );
17
31
  }
18
32
 
19
33
  function DropdownMenuContent({
20
34
  className,
21
35
  sideOffset = 4,
36
+ children,
22
37
  ...props
23
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
38
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Popup> & { sideOffset?: number }) {
24
39
  return (
25
40
  <DropdownMenuPrimitive.Portal>
26
- <DropdownMenuPrimitive.Content
27
- data-slot="dropdown-menu-content"
28
- sideOffset={sideOffset}
29
- className={cn(
30
- "bg-raised text-primary data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1",
31
- className,
32
- )}
33
- {...props}
34
- />
41
+ <DropdownMenuPrimitive.Positioner sideOffset={sideOffset}>
42
+ <DropdownMenuPrimitive.Popup
43
+ data-slot="dropdown-menu-content"
44
+ className={cn(
45
+ "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 z-50 max-h-(--available-height) min-w-[8rem] origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 outline-none",
46
+ className,
47
+ )}
48
+ {...props}
49
+ >
50
+ {children}
51
+ </DropdownMenuPrimitive.Popup>
52
+ </DropdownMenuPrimitive.Positioner>
35
53
  </DropdownMenuPrimitive.Portal>
36
54
  );
37
55
  }
@@ -44,9 +62,9 @@ function DropdownMenuLabel({
44
62
  className,
45
63
  inset,
46
64
  ...props
47
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & { inset?: boolean }) {
65
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.GroupLabel> & { inset?: boolean }) {
48
66
  return (
49
- <DropdownMenuPrimitive.Label
67
+ <DropdownMenuPrimitive.GroupLabel
50
68
  data-slot="dropdown-menu-label"
51
69
  data-inset={inset}
52
70
  className={cn("px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className)}
@@ -70,7 +88,7 @@ function DropdownMenuItem({
70
88
  data-inset={inset}
71
89
  data-variant={variant}
72
90
  className={cn(
73
- "focus:bg-accent focus:text-on-accent data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
91
+ "data-highlighted:bg-muted data-highlighted:text-muted-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:data-highlighted:bg-destructive/10 relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
74
92
  className,
75
93
  )}
76
94
  {...props}
@@ -88,16 +106,16 @@ function DropdownMenuCheckboxItem({
88
106
  <DropdownMenuPrimitive.CheckboxItem
89
107
  data-slot="dropdown-menu-checkbox-item"
90
108
  className={cn(
91
- "focus:bg-accent focus:text-on-accent relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
109
+ "data-highlighted:bg-muted data-highlighted:text-muted-foreground relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-disabled:pointer-events-none data-disabled:opacity-50",
92
110
  className,
93
111
  )}
94
112
  checked={checked}
95
113
  {...props}
96
114
  >
97
115
  <span className="absolute left-2 flex size-3.5 items-center justify-center">
98
- <DropdownMenuPrimitive.ItemIndicator>
116
+ <DropdownMenuPrimitive.CheckboxItemIndicator>
99
117
  <CheckIcon className="size-4" />
100
- </DropdownMenuPrimitive.ItemIndicator>
118
+ </DropdownMenuPrimitive.CheckboxItemIndicator>
101
119
  </span>
102
120
  {children}
103
121
  </DropdownMenuPrimitive.CheckboxItem>
@@ -119,15 +137,15 @@ function DropdownMenuRadioItem({
119
137
  <DropdownMenuPrimitive.RadioItem
120
138
  data-slot="dropdown-menu-radio-item"
121
139
  className={cn(
122
- "focus:bg-accent focus:text-on-accent relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
140
+ "data-highlighted:bg-muted data-highlighted:text-muted-foreground relative flex cursor-default items-center rounded-sm py-1.5 pr-2 pl-8 text-sm outline-none select-none data-disabled:pointer-events-none data-disabled:opacity-50",
123
141
  className,
124
142
  )}
125
143
  {...props}
126
144
  >
127
145
  <span className="absolute left-2 flex size-3.5 items-center justify-center">
128
- <DropdownMenuPrimitive.ItemIndicator>
146
+ <DropdownMenuPrimitive.RadioItemIndicator>
129
147
  <CircleIcon className="size-2 fill-current" />
130
- </DropdownMenuPrimitive.ItemIndicator>
148
+ </DropdownMenuPrimitive.RadioItemIndicator>
131
149
  </span>
132
150
  {children}
133
151
  </DropdownMenuPrimitive.RadioItem>
@@ -153,8 +171,8 @@ function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"spa
153
171
  );
154
172
  }
155
173
 
156
- function DropdownMenuSub(props: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
157
- return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
174
+ function DropdownMenuSub(props: React.ComponentProps<typeof DropdownMenuPrimitive.SubmenuRoot>) {
175
+ return <DropdownMenuPrimitive.SubmenuRoot data-slot="dropdown-menu-sub" {...props} />;
158
176
  }
159
177
 
160
178
  function DropdownMenuSubTrigger({
@@ -162,32 +180,32 @@ function DropdownMenuSubTrigger({
162
180
  inset,
163
181
  children,
164
182
  ...props
165
- }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & { inset?: boolean }) {
183
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.SubmenuTrigger> & { inset?: boolean }) {
166
184
  return (
167
- <DropdownMenuPrimitive.SubTrigger
185
+ <DropdownMenuPrimitive.SubmenuTrigger
168
186
  data-slot="dropdown-menu-sub-trigger"
169
187
  data-inset={inset}
170
188
  className={cn(
171
- "focus:bg-accent focus:text-on-accent data-[state=open]:bg-accent data-[state=open]:text-on-accent flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[inset]:pl-8",
189
+ "data-highlighted:bg-muted data-highlighted:text-muted-foreground data-open:bg-muted data-open:text-muted-foreground flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[inset]:pl-8",
172
190
  className,
173
191
  )}
174
192
  {...props}
175
193
  >
176
194
  {children}
177
195
  <ChevronRightIcon className="ml-auto size-4" />
178
- </DropdownMenuPrimitive.SubTrigger>
196
+ </DropdownMenuPrimitive.SubmenuTrigger>
179
197
  );
180
198
  }
181
199
 
182
200
  function DropdownMenuSubContent({
183
201
  className,
184
202
  ...props
185
- }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
203
+ }: React.ComponentProps<typeof DropdownMenuPrimitive.Popup>) {
186
204
  return (
187
- <DropdownMenuPrimitive.SubContent
205
+ <DropdownMenuPrimitive.Popup
188
206
  data-slot="dropdown-menu-sub-content"
189
207
  className={cn(
190
- "bg-raised text-primary data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg",
208
+ "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg outline-none",
191
209
  className,
192
210
  )}
193
211
  {...props}
@@ -20,8 +20,8 @@ export const Default: Story = {
20
20
  </PopoverTrigger>
21
21
  <PopoverContent>
22
22
  <div className="grid gap-1.5">
23
- <h4 className="text-primary leading-none font-medium">Dimensions</h4>
24
- <p className="text-secondary text-sm">Set the dimensions for the layer.</p>
23
+ <h4 className="text-foreground leading-none font-medium">Dimensions</h4>
24
+ <p className="text-muted-foreground text-sm">Set the dimensions for the layer.</p>
25
25
  </div>
26
26
  <div className="grid gap-4 pt-4">
27
27
  <div className="grid grid-cols-3 items-center gap-4">
@@ -1,4 +1,4 @@
1
- import * as PopoverPrimitive from "@radix-ui/react-popover";
1
+ import { Popover as PopoverPrimitive } from "@base-ui/react/popover";
2
2
  import * as React from "react";
3
3
 
4
4
  import { cn } from "../../lib/utils";
@@ -7,34 +7,63 @@ function Popover({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Root
7
7
  return <PopoverPrimitive.Root data-slot="popover" {...props} />;
8
8
  }
9
9
 
10
- function PopoverTrigger({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
11
- return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
10
+ function PopoverTrigger({
11
+ asChild,
12
+ children,
13
+ ...props
14
+ }: React.ComponentProps<typeof PopoverPrimitive.Trigger> & { asChild?: boolean }) {
15
+ return (
16
+ <PopoverPrimitive.Trigger
17
+ data-slot="popover-trigger"
18
+ {...(asChild && React.isValidElement(children)
19
+ ? { render: children as React.ReactElement }
20
+ : {})}
21
+ {...props}
22
+ >
23
+ {!asChild && children}
24
+ </PopoverPrimitive.Trigger>
25
+ );
12
26
  }
13
27
 
14
28
  function PopoverContent({
15
29
  className,
16
30
  align = "center",
17
31
  sideOffset = 4,
32
+ children,
33
+ side = "bottom",
34
+ avoidCollisions = true,
18
35
  ...props
19
- }: React.ComponentProps<typeof PopoverPrimitive.Content>) {
36
+ }: React.ComponentProps<typeof PopoverPrimitive.Popup> & {
37
+ align?: "start" | "center" | "end";
38
+ sideOffset?: number;
39
+ side?: "top" | "right" | "bottom" | "left";
40
+ avoidCollisions?: boolean;
41
+ }) {
20
42
  return (
21
43
  <PopoverPrimitive.Portal>
22
- <PopoverPrimitive.Content
23
- data-slot="popover-content"
44
+ <PopoverPrimitive.Positioner
24
45
  align={align}
46
+ side={side}
25
47
  sideOffset={sideOffset}
26
- className={cn(
27
- "bg-raised text-primary data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 outline-hidden",
28
- className,
29
- )}
30
- {...props}
31
- />
48
+ collisionAvoidance={avoidCollisions ? undefined : { side: "none", align: "none" }}
49
+ >
50
+ <PopoverPrimitive.Popup
51
+ data-slot="popover-content"
52
+ className={cn(
53
+ "bg-popover text-popover-foreground data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 z-50 w-72 origin-(--transform-origin) rounded-md border p-4 outline-hidden",
54
+ className,
55
+ )}
56
+ {...props}
57
+ >
58
+ {children}
59
+ </PopoverPrimitive.Popup>
60
+ </PopoverPrimitive.Positioner>
32
61
  </PopoverPrimitive.Portal>
33
62
  );
34
63
  }
35
64
 
36
- function PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {
37
- return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;
65
+ function PopoverAnchor({ ...props }: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {
66
+ return <PopoverPrimitive.Trigger data-slot="popover-anchor" {...props} />;
38
67
  }
39
68
 
40
69
  export { Popover, PopoverTrigger, PopoverContent, PopoverAnchor };
@@ -38,14 +38,14 @@ const SheetDemo = () => (
38
38
  <div className="flex flex-col gap-2">
39
39
  <span className="text-sm font-medium">Name</span>
40
40
  <input
41
- className="border-default placeholder:text-secondary focus-visible:ring-focus flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:ring-1 focus-visible:outline-none"
41
+ className="border-input placeholder:text-muted-foreground focus-visible:ring-ring flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:ring-1 focus-visible:outline-none"
42
42
  placeholder="Jane Doe"
43
43
  />
44
44
  </div>
45
45
  <div className="flex flex-col gap-2">
46
46
  <span className="text-sm font-medium">Email</span>
47
47
  <input
48
- className="border-default placeholder:text-secondary focus-visible:ring-focus flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:ring-1 focus-visible:outline-none"
48
+ className="border-input placeholder:text-muted-foreground focus-visible:ring-ring flex h-9 w-full rounded-md border bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:ring-1 focus-visible:outline-none"
49
49
  placeholder="jane@example.com"
50
50
  type="email"
51
51
  />