@kinetixui/ui 0.6.4 → 0.7.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 (46) hide show
  1. package/dist/index.d.ts +11 -1
  2. package/dist/index.js +395 -380
  3. package/package.json +2 -2
  4. package/src/components/accordion.tsx +1 -1
  5. package/src/components/alert-dialog.tsx +2 -2
  6. package/src/components/app-bar.tsx +1 -1
  7. package/src/components/audio-player.tsx +3 -0
  8. package/src/components/badge.tsx +13 -4
  9. package/src/components/button.tsx +2 -2
  10. package/src/components/calendar.tsx +9 -5
  11. package/src/components/checkbox.tsx +1 -1
  12. package/src/components/circular-progress.tsx +1 -1
  13. package/src/components/command.tsx +4 -2
  14. package/src/components/context-menu.tsx +2 -2
  15. package/src/components/dialog.tsx +3 -3
  16. package/src/components/drawer.tsx +2 -2
  17. package/src/components/dropdown-menu.tsx +4 -2
  18. package/src/components/fab.tsx +1 -1
  19. package/src/components/file-upload.tsx +1 -1
  20. package/src/components/hover-card.tsx +1 -1
  21. package/src/components/image.tsx +2 -2
  22. package/src/components/inform.tsx +1 -1
  23. package/src/components/input-group.tsx +2 -2
  24. package/src/components/input-otp.tsx +3 -3
  25. package/src/components/input.tsx +2 -2
  26. package/src/components/label.tsx +1 -1
  27. package/src/components/list.tsx +1 -1
  28. package/src/components/menubar.tsx +2 -2
  29. package/src/components/modal.tsx +2 -2
  30. package/src/components/navigation-menu.tsx +4 -4
  31. package/src/components/number-input.tsx +3 -3
  32. package/src/components/popover.tsx +1 -1
  33. package/src/components/radio-group.tsx +1 -1
  34. package/src/components/resizable.tsx +1 -1
  35. package/src/components/select.tsx +5 -3
  36. package/src/components/sheet.tsx +3 -3
  37. package/src/components/sidebar.tsx +5 -5
  38. package/src/components/slider.tsx +1 -1
  39. package/src/components/switch.tsx +1 -1
  40. package/src/components/tabs.tsx +1 -1
  41. package/src/components/tag.tsx +27 -15
  42. package/src/components/textarea.tsx +2 -2
  43. package/src/components/toggle.tsx +1 -1
  44. package/src/components/tooltip.tsx +1 -1
  45. package/src/components-behavior.test.tsx +37 -0
  46. package/tailwind.config.ts +29 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kinetixui/ui",
3
- "version": "0.6.4",
3
+ "version": "0.7.0",
4
4
  "private": false,
5
5
  "description": "KinetixUI React components — CVA + Radix + Tailwind on the KinetixUI token contract. Also distributed via the kinetixui CLI as a component registry.",
6
6
  "keywords": [
@@ -90,7 +90,7 @@
90
90
  "react-dom": ">=18"
91
91
  },
92
92
  "devDependencies": {
93
- "@kinetixui/tokens": "0.6.4",
93
+ "@kinetixui/tokens": "0.7.0",
94
94
  "@testing-library/jest-dom": "^7.0.1",
95
95
  "@testing-library/react": "^16.3.3",
96
96
  "@testing-library/user-event": "^14.6.7",
@@ -29,7 +29,7 @@ const AccordionTrigger = React.forwardRef<
29
29
  {...props}
30
30
  >
31
31
  {children}
32
- <ChevronDown className="size-4 shrink-0 text-muted-foreground transition-transform duration-200" />
32
+ <ChevronDown className="size-4 shrink-0 text-muted-foreground transition-transform duration-fast" />
33
33
  </AccordionPrimitive.Trigger>
34
34
  </AccordionPrimitive.Header>
35
35
  ));
@@ -16,7 +16,7 @@ const AlertDialogOverlay = React.forwardRef<
16
16
  <AlertDialogPrimitive.Overlay
17
17
  ref={ref}
18
18
  className={cn(
19
- "fixed inset-0 z-50 bg-foreground/40 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
19
+ "fixed inset-0 z-overlay bg-foreground/40 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
20
20
  className,
21
21
  )}
22
22
  {...props}
@@ -33,7 +33,7 @@ const AlertDialogContent = React.forwardRef<
33
33
  <AlertDialogPrimitive.Content
34
34
  ref={ref}
35
35
  className={cn(
36
- "fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg sm:rounded-xl font-sans",
36
+ "fixed left-1/2 top-1/2 z-overlay grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg sm:rounded-xl font-sans",
37
37
  "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",
38
38
  className,
39
39
  )}
@@ -48,7 +48,7 @@ const AppBar = React.forwardRef<HTMLElement, React.HTMLAttributes<HTMLElement>>(
48
48
  <header
49
49
  ref={ref}
50
50
  className={cn(
51
- "sticky top-0 z-40 w-full border-b border-border bg-background/95 font-sans",
51
+ "sticky top-0 z-sticky w-full border-b border-border bg-background/95 font-sans",
52
52
  "backdrop-blur supports-[backdrop-filter]:bg-background/75",
53
53
  className,
54
54
  )}
@@ -73,6 +73,9 @@ const AudioPlayer = React.forwardRef<HTMLDivElement, AudioPlayerProps>(
73
73
  </button>
74
74
  );
75
75
 
76
+ // disabled:opacity-40 (not --opacity-disabled's 0.5) is a real, pre-existing
77
+ // inconsistency — left as a literal rather than silently changing the
78
+ // rendered value; a design call, not a rename.
76
79
  const iconBtn =
77
80
  "grid size-8 shrink-0 place-items-center rounded-full text-muted-foreground outline-none transition-colors hover:bg-muted hover:text-foreground focus-visible:text-foreground disabled:opacity-40 [&>svg]:size-4";
78
81
 
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  import * as React from "react";
4
+ import { Slot } from "@radix-ui/react-slot";
4
5
  import { cva, type VariantProps } from "class-variance-authority";
5
6
  import { cn } from "../lib/utils";
6
7
 
@@ -31,10 +32,18 @@ const badgeVariants = cva(
31
32
 
32
33
  export interface BadgeProps
33
34
  extends React.HTMLAttributes<HTMLDivElement>,
34
- VariantProps<typeof badgeVariants> {}
35
-
36
- function Badge({ className, variant, ...props }: BadgeProps) {
37
- return <div className={cn(badgeVariants({ variant }), className)} {...props} />;
35
+ VariantProps<typeof badgeVariants> {
36
+ /** render as the single child element (Radix Slot) instead of <div> —
37
+ * e.g. `<Badge asChild><a href="/new">New</a></Badge>` for a linked badge */
38
+ asChild?: boolean;
38
39
  }
39
40
 
41
+ const Badge = React.forwardRef<HTMLDivElement, BadgeProps>(
42
+ ({ className, variant, asChild = false, ...props }, ref) => {
43
+ const Comp = asChild ? Slot : "div";
44
+ return <Comp ref={ref} className={cn(badgeVariants({ variant }), className)} {...props} />;
45
+ },
46
+ );
47
+ Badge.displayName = "Badge";
48
+
40
49
  export { Badge, badgeVariants };
@@ -28,7 +28,7 @@ const buttonVariants = cva(
28
28
  "font-sans font-medium whitespace-nowrap select-none",
29
29
  "rounded-md transition-colors outline-none",
30
30
  "focus-visible:shadow-focus",
31
- "disabled:pointer-events-none disabled:opacity-50",
31
+ "disabled:pointer-events-none disabled:opacity-disabled",
32
32
  "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg]:size-[18px]",
33
33
  ],
34
34
  {
@@ -72,7 +72,7 @@ const buttonVariants = cva(
72
72
  Hover: "",
73
73
  Focus: "shadow-focus",
74
74
  Active: "",
75
- Disabled: "pointer-events-none opacity-50",
75
+ Disabled: "pointer-events-none opacity-disabled",
76
76
  },
77
77
  /** corner style — matches the design source's Corners property */
78
78
  corners: {
@@ -19,13 +19,17 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C
19
19
  month_caption: "flex justify-center pt-1 relative items-center w-full",
20
20
  caption_label: "text-sm font-medium",
21
21
  nav: "flex items-center gap-1",
22
+ // Resting-dim/hover-brighten nav icons — numerically the disabled
23
+ // value (0.5) but not a disabled state, so left as a literal rather
24
+ // than forced into --opacity-disabled or --opacity-muted (0.7, which
25
+ // would change the rendered value).
22
26
  button_previous: cn(
23
27
  buttonVariants({ variant: "Outline", size: "icon" }),
24
- "absolute left-1 size-7 bg-transparent p-0 opacity-50 hover:opacity-100",
28
+ "absolute left-1 size-7 bg-transparent p-0 opacity-50 hover:opacity-visible",
25
29
  ),
26
30
  button_next: cn(
27
31
  buttonVariants({ variant: "Outline", size: "icon" }),
28
- "absolute right-1 size-7 bg-transparent p-0 opacity-50 hover:opacity-100",
32
+ "absolute right-1 size-7 bg-transparent p-0 opacity-50 hover:opacity-visible",
29
33
  ),
30
34
  month_grid: "w-full border-collapse space-x-1",
31
35
  weekdays: "flex",
@@ -36,19 +40,19 @@ function Calendar({ className, classNames, showOutsideDays = true, ...props }: C
36
40
  // accent background + corner rounding and hands the primary-colored
37
41
  // "selected" circle down to `day_button` via a `group` class.
38
42
  day: cn(
39
- "group relative p-0 text-center text-sm focus-within:relative focus-within:z-20 aria-selected:bg-accent",
43
+ "group relative p-0 text-center text-sm focus-within:relative focus-within:z-focus aria-selected:bg-accent",
40
44
  props.mode === "range" ? "" : "aria-selected:rounded-md",
41
45
  ),
42
46
  day_button: cn(
43
47
  buttonVariants({ variant: "Ghost", size: "icon" }),
44
- "size-8 p-0 font-normal group-aria-selected:opacity-100 group-data-[selected=true]:bg-primary group-data-[selected=true]:text-primary-foreground group-data-[selected=true]:hover:bg-primary group-data-[selected=true]:hover:text-primary-foreground group-data-[selected=true]:focus:bg-primary group-data-[selected=true]:focus:text-primary-foreground",
48
+ "size-8 p-0 font-normal group-aria-selected:opacity-visible group-data-[selected=true]:bg-primary group-data-[selected=true]:text-primary-foreground group-data-[selected=true]:hover:bg-primary group-data-[selected=true]:hover:text-primary-foreground group-data-[selected=true]:focus:bg-primary group-data-[selected=true]:focus:text-primary-foreground",
45
49
  ),
46
50
  range_start: "day-range-start rounded-l-md",
47
51
  range_end: "day-range-end rounded-r-md",
48
52
  range_middle: "aria-selected:bg-accent aria-selected:text-accent-foreground",
49
53
  today: "bg-accent text-accent-foreground",
50
54
  outside: "day-outside text-muted-foreground aria-selected:text-muted-foreground",
51
- disabled: "text-muted-foreground opacity-50",
55
+ disabled: "text-muted-foreground opacity-disabled",
52
56
  hidden: "invisible",
53
57
  ...classNames,
54
58
  }}
@@ -20,7 +20,7 @@ const Checkbox = React.forwardRef<
20
20
  className={cn(
21
21
  "peer size-[18px] shrink-0 rounded-sm border-2 border-input outline-none transition-colors",
22
22
  "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
23
- "disabled:cursor-not-allowed disabled:opacity-50",
23
+ "disabled:cursor-not-allowed disabled:opacity-disabled",
24
24
  "data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
25
25
  "data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground",
26
26
  "aria-invalid:border-destructive aria-invalid:data-[state=checked]:border-destructive aria-invalid:data-[state=checked]:bg-destructive",
@@ -52,7 +52,7 @@ const CircularProgress = React.forwardRef<HTMLDivElement, CircularProgressProps>
52
52
  strokeLinecap="round"
53
53
  strokeDasharray={c}
54
54
  strokeDashoffset={c - (v / 100) * c}
55
- className="stroke-primary transition-[stroke-dashoffset] duration-300"
55
+ className="stroke-primary transition-[stroke-dashoffset] duration-base"
56
56
  />
57
57
  </svg>
58
58
  {(showValue || label != null) && (
@@ -36,11 +36,13 @@ const CommandInput = React.forwardRef<
36
36
  React.ComponentPropsWithoutRef<typeof CommandPrimitive.Input>
37
37
  >(({ className, ...props }, ref) => (
38
38
  <div className="flex items-center border-b px-3" cmdk-input-wrapper="">
39
+ {/* Resting-dim icon, no hover/disabled state — numerically the disabled
40
+ value (0.5) but not that role; left as a literal, see calendar.tsx */}
39
41
  <Search className="mr-2 size-4 shrink-0 opacity-50" />
40
42
  <CommandPrimitive.Input
41
43
  ref={ref}
42
44
  className={cn(
43
- "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
45
+ "flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-disabled",
44
46
  className,
45
47
  )}
46
48
  {...props}
@@ -97,7 +99,7 @@ const CommandItem = React.forwardRef<
97
99
  <CommandPrimitive.Item
98
100
  ref={ref}
99
101
  className={cn(
100
- "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-50 [&_svg]:size-4 [&_svg]:shrink-0",
102
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none data-[disabled=true]:pointer-events-none data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground data-[disabled=true]:opacity-disabled [&_svg]:size-4 [&_svg]:shrink-0",
101
103
  className,
102
104
  )}
103
105
  {...props}
@@ -13,9 +13,9 @@ const ContextMenuSub = ContextMenuPrimitive.Sub;
13
13
  const ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
14
14
 
15
15
  const itemCls =
16
- "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
16
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-disabled";
17
17
  const contentCls =
18
- "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md font-sans 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";
18
+ "z-overlay min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md font-sans 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";
19
19
 
20
20
  const ContextMenuSubTrigger = React.forwardRef<
21
21
  React.ElementRef<typeof ContextMenuPrimitive.SubTrigger>,
@@ -17,7 +17,7 @@ const DialogOverlay = React.forwardRef<
17
17
  <DialogPrimitive.Overlay
18
18
  ref={ref}
19
19
  className={cn(
20
- "fixed inset-0 z-50 bg-foreground/40 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
20
+ "fixed inset-0 z-overlay bg-foreground/40 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
21
21
  className,
22
22
  )}
23
23
  {...props}
@@ -34,14 +34,14 @@ const DialogContent = React.forwardRef<
34
34
  <DialogPrimitive.Content
35
35
  ref={ref}
36
36
  className={cn(
37
- "fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg sm:rounded-xl font-sans",
37
+ "fixed left-1/2 top-1/2 z-overlay grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg sm:rounded-xl font-sans",
38
38
  "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",
39
39
  className,
40
40
  )}
41
41
  {...props}
42
42
  >
43
43
  {children}
44
- <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring disabled:pointer-events-none">
44
+ <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-muted transition-opacity hover:opacity-visible focus:outline-none focus:ring-2 focus:ring-ring disabled:pointer-events-none">
45
45
  <X className="size-4" />
46
46
  <span className="sr-only">Close</span>
47
47
  </DialogPrimitive.Close>
@@ -17,7 +17,7 @@ const DrawerOverlay = React.forwardRef<
17
17
  React.ElementRef<typeof DrawerPrimitive.Overlay>,
18
18
  React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Overlay>
19
19
  >(({ className, ...props }, ref) => (
20
- <DrawerPrimitive.Overlay ref={ref} className={cn("fixed inset-0 z-50 bg-foreground/40", className)} {...props} />
20
+ <DrawerPrimitive.Overlay ref={ref} className={cn("fixed inset-0 z-overlay bg-foreground/40", className)} {...props} />
21
21
  ));
22
22
  DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
23
23
 
@@ -30,7 +30,7 @@ const DrawerContent = React.forwardRef<
30
30
  <DrawerPrimitive.Content
31
31
  ref={ref}
32
32
  className={cn(
33
- "fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background font-sans",
33
+ "fixed inset-x-0 bottom-0 z-overlay mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background font-sans",
34
34
  className,
35
35
  )}
36
36
  {...props}
@@ -13,9 +13,9 @@ const DropdownMenuSub = DropdownMenuPrimitive.Sub;
13
13
  const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
14
14
 
15
15
  const contentCls =
16
- "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md font-sans 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";
16
+ "z-overlay min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md font-sans 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";
17
17
  const itemCls =
18
- "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
18
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-disabled";
19
19
 
20
20
  const DropdownMenuContent = React.forwardRef<
21
21
  React.ElementRef<typeof DropdownMenuPrimitive.Content>,
@@ -85,6 +85,8 @@ const DropdownMenuSeparator = React.forwardRef<
85
85
  ));
86
86
  DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
87
87
 
88
+ // opacity-60 (not --opacity-muted's 0.7) is a pre-existing inconsistency —
89
+ // left as a literal rather than silently changing the rendered value.
88
90
  const DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => (
89
91
  <span className={cn("ml-auto text-xs tracking-widest opacity-60", className)} {...props} />
90
92
  );
@@ -13,7 +13,7 @@ const fabVariants = cva(
13
13
  [
14
14
  "inline-flex shrink-0 items-center justify-center gap-2 rounded-full font-sans font-medium",
15
15
  "shadow-lg outline-none transition-colors",
16
- "focus-visible:shadow-focus disabled:pointer-events-none disabled:opacity-50",
16
+ "focus-visible:shadow-focus disabled:pointer-events-none disabled:opacity-disabled",
17
17
  "[&_svg]:pointer-events-none [&_svg]:shrink-0",
18
18
  ],
19
19
  {
@@ -84,7 +84,7 @@ const FileUpload = React.forwardRef<HTMLDivElement, FileUploadProps>(
84
84
  "flex flex-col items-center gap-2 rounded-md border border-dashed border-input p-6 text-center outline-none transition-colors",
85
85
  !disabled && "cursor-pointer hover:bg-accent/50 focus-visible:shadow-focus",
86
86
  dragging && "border-primary bg-accent",
87
- disabled && "pointer-events-none opacity-50",
87
+ disabled && "pointer-events-none opacity-disabled",
88
88
  )}
89
89
  >
90
90
  <Upload className="size-6 text-muted-foreground" aria-hidden />
@@ -16,7 +16,7 @@ const HoverCardContent = React.forwardRef<
16
16
  align={align}
17
17
  sideOffset={sideOffset}
18
18
  className={cn(
19
- "z-50 w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none font-sans",
19
+ "z-overlay w-64 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none font-sans",
20
20
  "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",
21
21
  className,
22
22
  )}
@@ -64,8 +64,8 @@ const Image = React.forwardRef<HTMLImageElement, ImageProps>(
64
64
  ref={setRef}
65
65
  alt={alt}
66
66
  className={cn(
67
- "size-full object-cover transition-opacity duration-300",
68
- status === "loaded" ? "opacity-100" : "opacity-0",
67
+ "size-full object-cover transition-opacity duration-base",
68
+ status === "loaded" ? "opacity-visible" : "opacity-hidden",
69
69
  )}
70
70
  onLoad={(e) => {
71
71
  setStatus("loaded");
@@ -68,7 +68,7 @@ const Inform = React.forwardRef<HTMLDivElement, InformProps>(
68
68
  type="button"
69
69
  onClick={onDismiss}
70
70
  aria-label="Dismiss"
71
- className="-m-0.5 shrink-0 rounded-[2px] p-0.5 opacity-70 outline-none transition-opacity hover:opacity-100 focus-visible:opacity-100 focus-visible:ring-1 focus-visible:ring-current"
71
+ className="-m-0.5 shrink-0 rounded-[2px] p-0.5 opacity-muted outline-none transition-opacity hover:opacity-visible focus-visible:opacity-visible focus-visible:ring-1 focus-visible:ring-current"
72
72
  >
73
73
  <X className="size-3.5" />
74
74
  </button>
@@ -24,7 +24,7 @@ const InputGroup = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDiv
24
24
  "flex w-full items-center overflow-hidden rounded-sm border border-input bg-background font-sans transition-colors",
25
25
  "focus-within:border-primary focus-within:shadow-focus",
26
26
  "has-[[aria-invalid=true]]:border-destructive has-[[aria-invalid=true]]:focus-within:shadow-focus-destructive",
27
- "has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50",
27
+ "has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-disabled",
28
28
  className,
29
29
  )}
30
30
  {...props}
@@ -85,7 +85,7 @@ const InputGroupButton = React.forwardRef<
85
85
  className={cn(
86
86
  "flex h-full shrink-0 items-center gap-1.5 self-stretch border-l border-input bg-muted px-3 text-label-md text-foreground",
87
87
  // base is bg-muted and muted==accent on dark, so the fill is a no-op there — the ring carries it
88
- "outline-none transition-colors hover:bg-accent hover:ring-1 hover:ring-inset hover:ring-ring focus-visible:bg-accent focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
88
+ "outline-none transition-colors hover:bg-accent hover:ring-1 hover:ring-inset hover:ring-ring focus-visible:bg-accent focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-disabled",
89
89
  "[&_svg]:size-4 [&_svg]:shrink-0",
90
90
  className,
91
91
  )}
@@ -11,7 +11,7 @@ const InputOTP = React.forwardRef<
11
11
  >(({ className, containerClassName, ...props }, ref) => (
12
12
  <OTPInput
13
13
  ref={ref}
14
- containerClassName={cn("flex items-center gap-2 has-[:disabled]:opacity-50", containerClassName)}
14
+ containerClassName={cn("flex items-center gap-2 has-[:disabled]:opacity-disabled", containerClassName)}
15
15
  className={cn("disabled:cursor-not-allowed", className)}
16
16
  {...props}
17
17
  />
@@ -35,7 +35,7 @@ const InputOTPSlot = React.forwardRef<
35
35
  ref={ref}
36
36
  className={cn(
37
37
  "relative flex size-9 items-center justify-center border-y border-r border-input text-sm shadow-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md",
38
- isActive && "z-10 ring-1 ring-ring",
38
+ isActive && "z-docked ring-1 ring-ring",
39
39
  className,
40
40
  )}
41
41
  {...props}
@@ -43,7 +43,7 @@ const InputOTPSlot = React.forwardRef<
43
43
  {char}
44
44
  {hasFakeCaret && (
45
45
  <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
46
- <div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" />
46
+ <div className="h-4 w-px animate-caret-blink bg-foreground duration-slower" />
47
47
  </div>
48
48
  )}
49
49
  </div>
@@ -26,7 +26,7 @@ const inputVariants = cva(
26
26
  "placeholder:text-muted-foreground",
27
27
  "outline-none transition-colors",
28
28
  "focus-visible:border-primary focus-visible:shadow-focus",
29
- "disabled:cursor-not-allowed disabled:opacity-50",
29
+ "disabled:cursor-not-allowed disabled:opacity-disabled",
30
30
  "aria-[invalid=true]:border-destructive aria-[invalid=true]:focus-visible:border-destructive aria-[invalid=true]:focus-visible:shadow-focus-destructive",
31
31
  ],
32
32
  {
@@ -35,7 +35,7 @@ const inputVariants = cva(
35
35
  Default: "",
36
36
  Focus: "border-primary shadow-focus",
37
37
  Error: "border-destructive",
38
- Disabled: "opacity-50 pointer-events-none",
38
+ Disabled: "opacity-disabled pointer-events-none",
39
39
  },
40
40
  /** corner style — matches the design source's Corners property */
41
41
  corners: {
@@ -6,7 +6,7 @@ import { cva, type VariantProps } from "class-variance-authority";
6
6
  import { cn } from "../lib/utils";
7
7
 
8
8
  const labelVariants = cva(
9
- "text-sm font-medium leading-none font-sans peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
9
+ "text-sm font-medium leading-none font-sans peer-disabled:cursor-not-allowed peer-disabled:opacity-muted",
10
10
  );
11
11
 
12
12
  const Label = React.forwardRef<
@@ -51,7 +51,7 @@ const ListItem = React.forwardRef<HTMLDivElement, ListItemProps>(
51
51
  // ring keeps the highlighted row perceptible where the accent fill is <3:1 (dark)
52
52
  interactive &&
53
53
  "cursor-pointer hover:bg-accent hover:ring-1 hover:ring-inset hover:ring-ring focus-visible:bg-accent focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring",
54
- disabled && "pointer-events-none opacity-50",
54
+ disabled && "pointer-events-none opacity-disabled",
55
55
  className,
56
56
  )}
57
57
  {...props}
@@ -22,9 +22,9 @@ const MenubarRadioGroup = (props: React.ComponentProps<typeof MenubarPrimitive.R
22
22
  );
23
23
 
24
24
  const itemCls =
25
- "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
25
+ "relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-disabled";
26
26
  const contentCls =
27
- "z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md font-sans data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95";
27
+ "z-overlay min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md font-sans data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95";
28
28
 
29
29
  const Menubar = React.forwardRef<
30
30
  React.ElementRef<typeof MenubarPrimitive.Root>,
@@ -68,7 +68,7 @@ function Modal({
68
68
  <DialogOverlay />
69
69
  <DialogPrimitive.Content
70
70
  className={cn(
71
- "fixed left-1/2 top-1/2 z-50 flex w-full max-w-[480px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl bg-background font-sans",
71
+ "fixed left-1/2 top-1/2 z-overlay flex w-full max-w-[480px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl bg-background font-sans",
72
72
  "shadow-[0_4px_24px_rgba(0,0,0,0.12)]",
73
73
  "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",
74
74
  className,
@@ -79,7 +79,7 @@ function Modal({
79
79
  {title}
80
80
  </DialogPrimitive.Title>
81
81
  <DialogPrimitive.Close
82
- className="rounded-sm opacity-70 outline-none transition-opacity hover:opacity-100 focus-visible:ring-2 focus-visible:ring-ring"
82
+ className="rounded-sm opacity-muted outline-none transition-opacity hover:opacity-visible focus-visible:ring-2 focus-visible:ring-ring"
83
83
  aria-label="Close"
84
84
  >
85
85
  <X className="size-5" />
@@ -12,7 +12,7 @@ const NavigationMenu = React.forwardRef<
12
12
  >(({ className, children, ...props }, ref) => (
13
13
  <NavigationMenuPrimitive.Root
14
14
  ref={ref}
15
- className={cn("relative z-10 flex max-w-max flex-1 items-center justify-center font-sans", className)}
15
+ className={cn("relative z-docked flex max-w-max flex-1 items-center justify-center font-sans", className)}
16
16
  {...props}
17
17
  >
18
18
  {children}
@@ -36,7 +36,7 @@ NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName;
36
36
  const NavigationMenuItem = NavigationMenuPrimitive.Item;
37
37
 
38
38
  const navigationMenuTriggerStyle = cva(
39
- "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[state=open]:bg-accent/50",
39
+ "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-disabled data-[state=open]:bg-accent/50",
40
40
  );
41
41
 
42
42
  const NavigationMenuTrigger = React.forwardRef<
@@ -46,7 +46,7 @@ const NavigationMenuTrigger = React.forwardRef<
46
46
  <NavigationMenuPrimitive.Trigger ref={ref} className={cn(navigationMenuTriggerStyle(), "group", className)} {...props}>
47
47
  {children}
48
48
  <ChevronDown
49
- className="relative top-px ml-1 size-3 transition-transform duration-200 group-data-[state=open]:rotate-180"
49
+ className="relative top-px ml-1 size-3 transition-transform duration-fast group-data-[state=open]:rotate-180"
50
50
  aria-hidden
51
51
  />
52
52
  </NavigationMenuPrimitive.Trigger>
@@ -94,7 +94,7 @@ const NavigationMenuIndicator = React.forwardRef<
94
94
  <NavigationMenuPrimitive.Indicator
95
95
  ref={ref}
96
96
  className={cn(
97
- "top-full z-[1] flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
97
+ "top-full z-raised flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
98
98
  className,
99
99
  )}
100
100
  {...props}
@@ -42,7 +42,7 @@ const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
42
42
  <div
43
43
  className={cn(
44
44
  "flex h-10 items-stretch overflow-hidden rounded-md border border-input font-sans focus-within:shadow-focus",
45
- disabled && "pointer-events-none opacity-50",
45
+ disabled && "pointer-events-none opacity-disabled",
46
46
  className,
47
47
  )}
48
48
  >
@@ -51,7 +51,7 @@ const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
51
51
  aria-label="Decrease"
52
52
  disabled={disabled || (min != null && current <= min)}
53
53
  onClick={() => set(current - step)}
54
- className="flex w-9 shrink-0 items-center justify-center text-muted-foreground outline-none transition-colors hover:bg-accent hover:text-foreground focus-visible:bg-accent focus-visible:text-foreground focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"
54
+ className="flex w-9 shrink-0 items-center justify-center text-muted-foreground outline-none transition-colors hover:bg-accent hover:text-foreground focus-visible:bg-accent focus-visible:text-foreground focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-disabled"
55
55
  >
56
56
  <Minus className="size-4" />
57
57
  </button>
@@ -76,7 +76,7 @@ const NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(
76
76
  aria-label="Increase"
77
77
  disabled={disabled || (max != null && current >= max)}
78
78
  onClick={() => set(current + step)}
79
- className="flex w-9 shrink-0 items-center justify-center text-muted-foreground outline-none transition-colors hover:bg-accent hover:text-foreground focus-visible:bg-accent focus-visible:text-foreground focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"
79
+ className="flex w-9 shrink-0 items-center justify-center text-muted-foreground outline-none transition-colors hover:bg-accent hover:text-foreground focus-visible:bg-accent focus-visible:text-foreground focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-disabled"
80
80
  >
81
81
  <Plus className="size-4" />
82
82
  </button>
@@ -18,7 +18,7 @@ const PopoverContent = React.forwardRef<
18
18
  align={align}
19
19
  sideOffset={sideOffset}
20
20
  className={cn(
21
- "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none font-sans",
21
+ "z-overlay w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-none font-sans",
22
22
  "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",
23
23
  "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",
24
24
  className,
@@ -27,7 +27,7 @@ const RadioGroupItem = React.forwardRef<
27
27
  className={cn(
28
28
  "aspect-square size-[18px] rounded-full border-2 border-input text-primary outline-none transition-colors",
29
29
  "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
30
- "disabled:cursor-not-allowed disabled:opacity-50",
30
+ "disabled:cursor-not-allowed disabled:opacity-disabled",
31
31
  "data-[state=checked]:border-primary",
32
32
  "aria-invalid:border-destructive",
33
33
  className,
@@ -51,7 +51,7 @@ const ResizableHandle = ({
51
51
  {...props}
52
52
  >
53
53
  {withHandle && (
54
- <div className="z-10 flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
54
+ <div className="z-docked flex h-4 w-3 items-center justify-center rounded-sm border bg-border">
55
55
  <GripVertical className="size-2.5" />
56
56
  </div>
57
57
  )}
@@ -28,13 +28,15 @@ const SelectTrigger = React.forwardRef<
28
28
  "focus:border-primary focus:shadow-focus",
29
29
  "data-[state=open]:border-primary",
30
30
  "aria-[invalid=true]:border-destructive aria-[invalid=true]:focus:border-destructive aria-[invalid=true]:focus:shadow-focus-destructive",
31
- "disabled:cursor-not-allowed disabled:opacity-50",
31
+ "disabled:cursor-not-allowed disabled:opacity-disabled",
32
32
  className,
33
33
  )}
34
34
  {...props}
35
35
  >
36
36
  {children}
37
37
  <SelectPrimitive.Icon asChild>
38
+ {/* opacity-60 (not --opacity-muted's 0.7) is a pre-existing
39
+ inconsistency — left as a literal, see dropdown-menu.tsx */}
38
40
  <ChevronDown className="size-4 shrink-0 opacity-60" />
39
41
  </SelectPrimitive.Icon>
40
42
  </SelectPrimitive.Trigger>
@@ -50,7 +52,7 @@ const SelectContent = React.forwardRef<
50
52
  ref={ref}
51
53
  position={position}
52
54
  className={cn(
53
- "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md font-sans",
55
+ "relative z-overlay max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md font-sans",
54
56
  "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",
55
57
  position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1",
56
58
  className,
@@ -89,7 +91,7 @@ const SelectItem = React.forwardRef<
89
91
  ref={ref}
90
92
  className={cn(
91
93
  "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none",
92
- "focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
94
+ "focus:bg-accent focus:text-accent-foreground focus:ring-1 focus:ring-inset focus:ring-ring data-[disabled]:pointer-events-none data-[disabled]:opacity-disabled",
93
95
  className,
94
96
  )}
95
97
  {...props}