atlas-react-kit 0.4.1 → 0.6.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 (77) hide show
  1. package/COMPONENTS.md +135 -41
  2. package/DESIGN_RULES.md +79 -20
  3. package/README.md +60 -11
  4. package/dist/accordion.d.ts +8 -0
  5. package/dist/accordion.js +20 -0
  6. package/dist/alert.d.ts +19 -0
  7. package/dist/alert.js +37 -0
  8. package/dist/avatar.d.ts +9 -0
  9. package/dist/avatar.js +16 -0
  10. package/dist/badge.d.ts +2 -2
  11. package/dist/badge.js +1 -1
  12. package/dist/breadcrumb.d.ts +16 -0
  13. package/dist/breadcrumb.js +33 -0
  14. package/dist/button.d.ts +6 -5
  15. package/dist/button.js +7 -6
  16. package/dist/calendar.d.ts +1 -1
  17. package/dist/calendar.js +20 -2
  18. package/dist/checkbox.js +1 -1
  19. package/dist/color-legend.d.ts +22 -0
  20. package/dist/color-legend.js +11 -0
  21. package/dist/combobox.d.ts +4 -1
  22. package/dist/combobox.js +11 -5
  23. package/dist/date-picker.js +14 -3
  24. package/dist/date-time-picker.d.ts +36 -0
  25. package/dist/date-time-picker.js +105 -0
  26. package/dist/dropdown-menu.d.ts +26 -0
  27. package/dist/dropdown-menu.js +56 -0
  28. package/dist/error-boundary.d.ts +1 -1
  29. package/dist/field.js +1 -1
  30. package/dist/form/date-time-picker-field.d.ts +16 -0
  31. package/dist/form/date-time-picker-field.js +12 -0
  32. package/dist/form/markdown-field.d.ts +17 -0
  33. package/dist/form/markdown-field.js +26 -0
  34. package/dist/form/text-field.d.ts +7 -1
  35. package/dist/form/text-field.js +3 -3
  36. package/dist/info-tip.d.ts +27 -0
  37. package/dist/info-tip.js +63 -0
  38. package/dist/input.js +1 -1
  39. package/dist/internal/form-control.d.ts +1 -1
  40. package/dist/internal/form-control.js +1 -1
  41. package/dist/internal/tabs-styles.js +3 -3
  42. package/dist/markdown.d.ts +18 -0
  43. package/dist/markdown.js +62 -0
  44. package/dist/meter.d.ts +22 -0
  45. package/dist/meter.js +17 -0
  46. package/dist/pagination.d.ts +18 -0
  47. package/dist/pagination.js +35 -0
  48. package/dist/progress.d.ts +8 -0
  49. package/dist/progress.js +13 -0
  50. package/dist/radio-group.d.ts +13 -0
  51. package/dist/radio-group.js +20 -0
  52. package/dist/search-picker.d.ts +30 -0
  53. package/dist/search-picker.js +100 -0
  54. package/dist/select.js +2 -2
  55. package/dist/slider.d.ts +20 -0
  56. package/dist/slider.js +20 -0
  57. package/dist/spinner.d.ts +8 -0
  58. package/dist/spinner.js +13 -0
  59. package/dist/stat-tile.d.ts +21 -0
  60. package/dist/stat-tile.js +17 -0
  61. package/dist/switch.d.ts +9 -0
  62. package/dist/switch.js +14 -0
  63. package/dist/table.d.ts +2 -0
  64. package/dist/table.js +26 -2
  65. package/dist/tabs.d.ts +6 -3
  66. package/dist/tabs.js +5 -2
  67. package/dist/textarea.js +1 -1
  68. package/dist/theme-provider.d.ts +2 -1
  69. package/dist/theme-provider.js +28 -4
  70. package/dist/toggle-group.js +2 -2
  71. package/dist/toggle.d.ts +11 -1
  72. package/dist/toggle.js +13 -3
  73. package/dist/tooltip.d.ts +15 -0
  74. package/dist/tooltip.js +26 -0
  75. package/package.json +92 -5
  76. package/skills/atlas-react-kit/SKILL.md +148 -0
  77. package/theme.css +28 -14
package/dist/select.js CHANGED
@@ -14,7 +14,7 @@ function SelectValue({ ...props }) {
14
14
  return _jsx(SelectPrimitive.Value, { "data-slot": "select-value", ...props });
15
15
  }
16
16
  function SelectTrigger({ className, size = "default", children, ...props }) {
17
- return (_jsxs(SelectPrimitive.Trigger, { "data-slot": "select-trigger", "data-size": size, className: cn("flex w-full min-w-0 items-center justify-between gap-1.5 rounded-none border border-input bg-background px-3 py-2 text-base whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-10 data-[size=sm]:h-9 md:text-sm *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDownIcon, { className: "pointer-events-none size-3.5 text-muted-foreground" }) })] }));
17
+ return (_jsxs(SelectPrimitive.Trigger, { "data-slot": "select-trigger", "data-size": size, className: cn("flex w-full min-w-0 items-center justify-between gap-1.5 rounded-none border border-input bg-background px-3 py-2 text-base whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-10 data-[size=sm]:h-9 md:text-sm *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5", className), ...props, children: [children, _jsx(SelectPrimitive.Icon, { asChild: true, children: _jsx(ChevronDownIcon, { className: "pointer-events-none size-3.5 text-muted-foreground" }) })] }));
18
18
  }
19
19
  function SelectContent({ className, children, position = "popper", align = "center", sideOffset = 4, collisionPadding = 8, ...props }) {
20
20
  return (_jsx(SelectPrimitive.Portal, { children: _jsxs(SelectPrimitive.Content, { "data-slot": "select-content", "data-align-trigger": position === "item-aligned", className: cn("relative z-50 max-h-(--radix-select-content-available-height) min-w-(--radix-select-trigger-width) origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-none bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none 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 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", position === "popper" &&
@@ -24,7 +24,7 @@ function SelectLabel({ className, ...props }) {
24
24
  return (_jsx(SelectPrimitive.Label, { "data-slot": "select-label", className: cn("px-3 py-2 text-xs font-semibold tracking-wider text-muted-foreground uppercase", className), ...props }));
25
25
  }
26
26
  function SelectItem({ className, children, ...props }) {
27
- return (_jsxs(SelectPrimitive.Item, { "data-slot": "select-item", className: cn("relative flex w-full cursor-default items-center gap-2.5 rounded-none py-2 pr-8 pl-3 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-[state=checked]:bg-selected data-[state=checked]:text-selected-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", className), ...props, children: [_jsx("span", { className: "pointer-events-none absolute right-2 flex size-4 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(CheckIcon, { className: "pointer-events-none" }) }) }), _jsx(SelectPrimitive.ItemText, { children: children })] }));
27
+ return (_jsxs(SelectPrimitive.Item, { "data-slot": "select-item", className: cn("relative flex w-full cursor-default items-center gap-2.5 rounded-none py-2 pr-8 pl-3 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:shadow-[inset_2px_0_0_var(--color-ring)] not-data-[variant=destructive]:focus:**:text-accent-foreground data-[state=checked]:bg-selected data-[state=checked]:text-selected-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", className), ...props, children: [_jsx("span", { className: "pointer-events-none absolute right-2 flex size-4 items-center justify-center", children: _jsx(SelectPrimitive.ItemIndicator, { children: _jsx(CheckIcon, { className: "pointer-events-none" }) }) }), _jsx(SelectPrimitive.ItemText, { children: children })] }));
28
28
  }
29
29
  function SelectSeparator({ className, ...props }) {
30
30
  return (_jsx(SelectPrimitive.Separator, { "data-slot": "select-separator", className: cn("pointer-events-none -mx-1.5 my-1.5 h-px bg-border/50", className), ...props }));
@@ -0,0 +1,20 @@
1
+ export type SliderProps = {
2
+ value: number;
3
+ onValueChange: (value: number) => void;
4
+ onValueCommit?: (value: number) => void;
5
+ min?: number;
6
+ max?: number;
7
+ step?: number;
8
+ disabled?: boolean;
9
+ id?: string;
10
+ name?: string;
11
+ className?: string;
12
+ "aria-label"?: string;
13
+ "aria-labelledby"?: string;
14
+ };
15
+ /**
16
+ * Single-thumb range on Radix Slider. Square track and thumb, primary fill,
17
+ * solid focus ring, 44px thumb hit area. Pair with a visible `Label`.
18
+ */
19
+ declare function Slider({ value, onValueChange, onValueCommit, min, max, step, disabled, id, name, className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, }: SliderProps): import("react").JSX.Element;
20
+ export { Slider };
package/dist/slider.js ADDED
@@ -0,0 +1,20 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Slider as SliderPrimitive } from "radix-ui";
4
+ import { cn } from "./utils.js";
5
+ /**
6
+ * Single-thumb range on Radix Slider. Square track and thumb, primary fill,
7
+ * solid focus ring, 44px thumb hit area. Pair with a visible `Label`.
8
+ */
9
+ function Slider({ value, onValueChange, onValueCommit, min = 0, max = 100, step = 1, disabled, id, name, className, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, }) {
10
+ return (_jsxs(SliderPrimitive.Root, { "data-slot": "slider", name: name, min: min, max: max, step: step, disabled: disabled, value: [value], onValueChange: (next) => {
11
+ const amount = next[0];
12
+ if (amount !== undefined)
13
+ onValueChange(amount);
14
+ }, onValueCommit: (next) => {
15
+ const amount = next[0];
16
+ if (amount !== undefined)
17
+ onValueCommit?.(amount);
18
+ }, className: cn("relative flex w-full touch-none items-center select-none data-disabled:opacity-50", className), children: [_jsx(SliderPrimitive.Track, { "data-slot": "slider-track", className: "relative h-1.5 w-full grow overflow-hidden rounded-none bg-muted", children: _jsx(SliderPrimitive.Range, { "data-slot": "slider-range", className: "absolute h-full bg-primary" }) }), _jsx(SliderPrimitive.Thumb, { id: id, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "data-slot": "slider-thumb", className: "relative block size-4 shrink-0 rounded-none border border-primary bg-background shadow-xs outline-none after:absolute after:top-1/2 after:left-1/2 after:size-11 after:-translate-x-1/2 after:-translate-y-1/2 focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring" })] }));
19
+ }
20
+ export { Slider };
@@ -0,0 +1,8 @@
1
+ import * as React from "react";
2
+ /**
3
+ * Indeterminate loading indicator for a button or small region. Announced as
4
+ * "Loading" by default; pass `aria-label` for something more specific. Use
5
+ * `Skeleton` or `TableBodySkeleton` when the layout of the result is known.
6
+ */
7
+ declare function Spinner({ className, "aria-label": ariaLabel, ...props }: React.ComponentProps<"svg">): React.JSX.Element;
8
+ export { Spinner };
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { Loader2Icon } from "lucide-react";
4
+ import { cn } from "./utils.js";
5
+ /**
6
+ * Indeterminate loading indicator for a button or small region. Announced as
7
+ * "Loading" by default; pass `aria-label` for something more specific. Use
8
+ * `Skeleton` or `TableBodySkeleton` when the layout of the result is known.
9
+ */
10
+ function Spinner({ className, "aria-label": ariaLabel = "Loading", ...props }) {
11
+ return (_jsx(Loader2Icon, { role: "status", "aria-label": ariaLabel, "data-slot": "spinner", className: cn("size-4 animate-spin", className), ...props }));
12
+ }
13
+ export { Spinner };
@@ -0,0 +1,21 @@
1
+ import type * as React from "react";
2
+ /**
3
+ * Uppercase muted label used on tiles and thin section chrome. Apps that
4
+ * only needed a heading class can import this instead of a wrapper.
5
+ */
6
+ export declare const sectionLabel = "text-xs font-semibold tracking-widest text-muted-foreground uppercase";
7
+ /** Row of tiles: stacked on phones, divided columns from `sm`. */
8
+ export declare const statTileGrid = "grid ring-1 ring-foreground/5 sm:divide-x";
9
+ export type StatTileProps = {
10
+ label: string;
11
+ value: string;
12
+ sub?: React.ReactNode;
13
+ children?: React.ReactNode;
14
+ className?: string;
15
+ };
16
+ /**
17
+ * Label, big tabular figure, optional extra (a meter), muted sub line.
18
+ * Domain copy and status colors stay in the app.
19
+ */
20
+ declare function StatTile({ label, value, sub, children, className }: StatTileProps): React.JSX.Element;
21
+ export { StatTile };
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "./utils.js";
3
+ /**
4
+ * Uppercase muted label used on tiles and thin section chrome. Apps that
5
+ * only needed a heading class can import this instead of a wrapper.
6
+ */
7
+ export const sectionLabel = "text-xs font-semibold tracking-widest text-muted-foreground uppercase";
8
+ /** Row of tiles: stacked on phones, divided columns from `sm`. */
9
+ export const statTileGrid = "grid ring-1 ring-foreground/5 sm:divide-x";
10
+ /**
11
+ * Label, big tabular figure, optional extra (a meter), muted sub line.
12
+ * Domain copy and status colors stay in the app.
13
+ */
14
+ function StatTile({ label, value, sub, children, className }) {
15
+ return (_jsxs("div", { "data-slot": "stat-tile", className: cn("flex flex-col gap-1 px-3 py-2", className), children: [_jsx("span", { className: sectionLabel, children: label }), _jsx("span", { className: "text-lg font-semibold tracking-tight tabular-nums", children: value }), children, sub ? (_jsx("span", { className: "text-xs leading-snug text-muted-foreground", children: sub })) : null] }));
16
+ }
17
+ export { StatTile };
@@ -0,0 +1,9 @@
1
+ import * as React from "react";
2
+ import { Switch as SwitchPrimitive } from "radix-ui";
3
+ /**
4
+ * On/off setting that applies immediately (notifications, dark mode). Use
5
+ * `Checkbox` for choices submitted with a form. Square like the other
6
+ * primitives; checked uses the primary fill, as `Checkbox` does.
7
+ */
8
+ declare function Switch({ className, ...props }: React.ComponentProps<typeof SwitchPrimitive.Root>): React.JSX.Element;
9
+ export { Switch };
package/dist/switch.js ADDED
@@ -0,0 +1,14 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Switch as SwitchPrimitive } from "radix-ui";
5
+ import { cn } from "./utils.js";
6
+ /**
7
+ * On/off setting that applies immediately (notifications, dark mode). Use
8
+ * `Checkbox` for choices submitted with a form. Square like the other
9
+ * primitives; checked uses the primary fill, as `Checkbox` does.
10
+ */
11
+ function Switch({ className, ...props }) {
12
+ return (_jsx(SwitchPrimitive.Root, { "data-slot": "switch", className: cn("peer inline-flex h-5 w-9 shrink-0 items-center rounded-none border border-transparent bg-input p-0.5 shadow-xs transition-colors outline-none focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 data-checked:bg-primary dark:data-checked:bg-primary", className), ...props, children: _jsx(SwitchPrimitive.Thumb, { "data-slot": "switch-thumb", className: "pointer-events-none block size-3.5 rounded-none bg-background shadow-sm ring-0 transition-transform data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-primary-foreground" }) }));
13
+ }
14
+ export { Switch };
package/dist/table.d.ts CHANGED
@@ -2,6 +2,8 @@ import * as React from "react";
2
2
  /**
3
3
  * Data table. The root is a `min-w-0 overflow-x-auto` scroller so nowrap
4
4
  * columns scroll inside the table instead of widening a card or panel.
5
+ * While it overflows, the scroller joins the tab order so keyboard users can
6
+ * scroll it with the arrow keys (WCAG 2.1.1).
5
7
  * Cell padding is `px-2` below `sm` and the original `p-3` from `sm` up.
6
8
  */
7
9
  declare function Table({ className, ...props }: React.ComponentProps<"table">): React.JSX.Element;
package/dist/table.js CHANGED
@@ -2,13 +2,37 @@
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { cn } from "./utils.js";
5
+ /**
6
+ * True while the element's content is wider than its box. Re-checked when
7
+ * the container or its content resizes.
8
+ */
9
+ function useHorizontalOverflow(ref) {
10
+ const [overflowing, setOverflowing] = React.useState(false);
11
+ React.useEffect(() => {
12
+ const element = ref.current;
13
+ if (!element || typeof ResizeObserver === "undefined")
14
+ return;
15
+ const check = () => setOverflowing(element.scrollWidth > element.clientWidth + 1);
16
+ check();
17
+ const observer = new ResizeObserver(check);
18
+ observer.observe(element);
19
+ if (element.firstElementChild)
20
+ observer.observe(element.firstElementChild);
21
+ return () => observer.disconnect();
22
+ }, [ref]);
23
+ return overflowing;
24
+ }
5
25
  /**
6
26
  * Data table. The root is a `min-w-0 overflow-x-auto` scroller so nowrap
7
27
  * columns scroll inside the table instead of widening a card or panel.
28
+ * While it overflows, the scroller joins the tab order so keyboard users can
29
+ * scroll it with the arrow keys (WCAG 2.1.1).
8
30
  * Cell padding is `px-2` below `sm` and the original `p-3` from `sm` up.
9
31
  */
10
32
  function Table({ className, ...props }) {
11
- return (_jsx("div", { "data-slot": "table-container", className: "relative w-full min-w-0 max-w-full overflow-x-auto overscroll-x-contain", children: _jsx("table", { "data-slot": "table", className: cn("w-full min-w-full caption-bottom text-sm", className), ...props }) }));
33
+ const containerRef = React.useRef(null);
34
+ const scrollable = useHorizontalOverflow(containerRef);
35
+ return (_jsx("div", { ref: containerRef, "data-slot": "table-container", tabIndex: scrollable ? 0 : undefined, className: "relative w-full min-w-0 max-w-full overflow-x-auto overscroll-x-contain outline-none focus-visible:ring-2 focus-visible:ring-ring", children: _jsx("table", { "data-slot": "table", className: cn("w-full min-w-full caption-bottom text-sm", className), ...props }) }));
12
36
  }
13
37
  function TableHeader({ className, ...props }) {
14
38
  return (_jsx("thead", { "data-slot": "table-header", className: cn("[&_tr]:border-b", className), ...props }));
@@ -20,7 +44,7 @@ function TableFooter({ className, ...props }) {
20
44
  return (_jsx("tfoot", { "data-slot": "table-footer", className: cn("border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", className), ...props }));
21
45
  }
22
46
  function TableRow({ className, ...props }) {
23
- return (_jsx("tr", { "data-slot": "table-row", className: cn("border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-selected", className), ...props }));
47
+ return (_jsx("tr", { "data-slot": "table-row", className: cn("border-b transition-colors hover:bg-muted/50 has-aria-expanded:bg-muted/50 data-[state=selected]:bg-selected data-[state=selected]:shadow-[inset_2px_0_0_var(--color-selected-foreground)]", className), ...props }));
24
48
  }
25
49
  function TableHead({ className, ...props }) {
26
50
  return (_jsx("th", { "data-slot": "table-head", className: cn("h-10 px-2 text-left align-middle text-xs font-medium tracking-wider whitespace-nowrap text-muted-foreground uppercase sm:h-12 sm:px-3 [&:has([role=checkbox])]:pr-0", className), ...props }));
package/dist/tabs.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React from "react";
2
2
  import { type VariantProps } from "class-variance-authority";
3
3
  import { Tabs as TabsPrimitive } from "radix-ui";
4
- import { tabsListVariants } from "./internal/tabs-styles.js";
4
+ import { tabsListVariants, tabsTriggerVariants } from "./internal/tabs-styles.js";
5
5
  declare function Tabs({ className, orientation, ...props }: React.ComponentProps<typeof TabsPrimitive.Root>): React.JSX.Element;
6
6
  declare function TabsList({ className, variant, ...props }: React.ComponentProps<typeof TabsPrimitive.List> & VariantProps<typeof tabsListVariants>): React.JSX.Element;
7
7
  declare function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>): React.JSX.Element;
@@ -9,9 +9,12 @@ declare function TabsContent({ className, ...props }: React.ComponentProps<typeo
9
9
  /**
10
10
  * Tab-styled links for a view switcher that changes the URL. Use this, not
11
11
  * `Tabs` + `TabsTrigger asChild`, when there are no `TabsContent` panels:
12
- * tabs would point assistive tech at panels that don't exist.
12
+ * tabs would point assistive tech at panels that don't exist. Style each link
13
+ * with `tabsTriggerVariants()` and mark the active one with
14
+ * `data-state="active"` and `aria-current="page"` (the Next adapter
15
+ * `TabsNavLink` does this for you).
13
16
  */
14
17
  declare function TabsNav({ className, children, ...props }: React.ComponentProps<"nav"> & {
15
18
  "aria-label": string;
16
19
  }): React.JSX.Element;
17
- export { Tabs, TabsList, TabsTrigger, TabsContent, TabsNav, tabsListVariants };
20
+ export { Tabs, TabsList, TabsTrigger, TabsContent, TabsNav, tabsListVariants, tabsTriggerVariants, };
package/dist/tabs.js CHANGED
@@ -20,9 +20,12 @@ function TabsContent({ className, ...props }) {
20
20
  /**
21
21
  * Tab-styled links for a view switcher that changes the URL. Use this, not
22
22
  * `Tabs` + `TabsTrigger asChild`, when there are no `TabsContent` panels:
23
- * tabs would point assistive tech at panels that don't exist.
23
+ * tabs would point assistive tech at panels that don't exist. Style each link
24
+ * with `tabsTriggerVariants()` and mark the active one with
25
+ * `data-state="active"` and `aria-current="page"` (the Next adapter
26
+ * `TabsNavLink` does this for you).
24
27
  */
25
28
  function TabsNav({ className, children, ...props }) {
26
29
  return (_jsx("nav", { "data-slot": "tabs-nav", "data-orientation": "horizontal", className: cn("group/tabs", className), ...props, children: _jsx("div", { "data-slot": "tabs-list", "data-variant": "default", className: tabsListVariants(), children: children }) }));
27
30
  }
28
- export { Tabs, TabsList, TabsTrigger, TabsContent, TabsNav, tabsListVariants };
31
+ export { Tabs, TabsList, TabsTrigger, TabsContent, TabsNav, tabsListVariants, tabsTriggerVariants, };
package/dist/textarea.js CHANGED
@@ -2,6 +2,6 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import * as React from "react";
3
3
  import { cn } from "./utils.js";
4
4
  function Textarea({ className, ...props }) {
5
- return (_jsx("textarea", { "data-slot": "textarea", className: cn("flex field-sizing-content min-h-16 w-full resize-none rounded-none border border-input bg-background px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/30 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 md:text-sm dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className), ...props }));
5
+ return (_jsx("textarea", { "data-slot": "textarea", className: cn("flex field-sizing-content min-h-16 w-full resize-none rounded-none border border-input bg-background px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-2 aria-invalid:ring-destructive/20 md:text-sm dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className), ...props }));
6
6
  }
7
7
  export { Textarea };
@@ -3,7 +3,8 @@ import { useTheme, type ThemeProviderProps as NextThemesProviderProps } from "ne
3
3
  export type ThemeProviderProps = NextThemesProviderProps & {
4
4
  /**
5
5
  * When true, pressing `d` toggles dark/light unless focus is in an editable
6
- * field. Defaults to true.
6
+ * field or a widget with letter typeahead (such as `Select`). Defaults to
7
+ * true.
7
8
  */
8
9
  enableShortcut?: boolean;
9
10
  };
@@ -2,22 +2,46 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import { ThemeProvider as NextThemesProvider, useTheme, } from "next-themes";
5
- const EDITABLE_SELECTOR = "input, textarea, select, [contenteditable]:not([contenteditable='false'])";
6
- function isEditableTarget(target) {
5
+ /**
6
+ * Targets where a bare `d` is input, not a shortcut: native and ARIA text
7
+ * fields, plus widgets with letter typeahead (Select trigger and list, menus,
8
+ * trees, grids).
9
+ */
10
+ const KEY_CONSUMING_SELECTOR = [
11
+ "input",
12
+ "textarea",
13
+ "select",
14
+ "[contenteditable]:not([contenteditable='false'])",
15
+ ...[
16
+ "textbox",
17
+ "searchbox",
18
+ "spinbutton",
19
+ "combobox",
20
+ "listbox",
21
+ "menu",
22
+ "menubar",
23
+ "tree",
24
+ "treegrid",
25
+ "grid",
26
+ ].map((role) => `[role="${role}"]`),
27
+ ].join(", ");
28
+ function isKeyConsumingTarget(target) {
7
29
  if (!(target instanceof HTMLElement))
8
30
  return false;
9
- return Boolean(target.closest(EDITABLE_SELECTOR));
31
+ return Boolean(target.closest(KEY_CONSUMING_SELECTOR));
10
32
  }
11
33
  /** Listens for the optional `d` shortcut inside an existing theme context. */
12
34
  function ThemeShortcut() {
13
35
  const { resolvedTheme, setTheme } = useTheme();
14
36
  React.useEffect(() => {
15
37
  function onKeyDown(event) {
38
+ if (event.defaultPrevented || event.repeat || event.isComposing)
39
+ return;
16
40
  if (event.metaKey || event.ctrlKey || event.altKey)
17
41
  return;
18
42
  if (event.key !== "d" && event.key !== "D")
19
43
  return;
20
- if (isEditableTarget(event.target))
44
+ if (isKeyConsumingTarget(event.target))
21
45
  return;
22
46
  event.preventDefault();
23
47
  setTheme(resolvedTheme === "dark" ? "light" : "dark");
@@ -16,9 +16,9 @@ function ToggleGroup({ className, variant, size, spacing = 2, orientation = "hor
16
16
  }
17
17
  function ToggleGroupItem({ className, children, variant = "default", size = "default", ...props }) {
18
18
  const context = React.useContext(ToggleGroupContext);
19
- return (_jsx(ToggleGroupPrimitive.Item, { "data-slot": "toggle-group-item", "data-variant": context.variant || variant, "data-size": context.size || size, "data-spacing": context.spacing, className: cn("shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-none data-[state=on]:bg-selected data-[state=on]:text-selected-foreground group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t", toggleVariants({
19
+ return (_jsx(ToggleGroupPrimitive.Item, { "data-slot": "toggle-group-item", "data-variant": context.variant || variant, "data-size": context.size || size, "data-spacing": context.spacing, className: cn(toggleVariants({
20
20
  variant: context.variant || variant,
21
21
  size: context.size || size,
22
- }), className), ...props, children: children }));
22
+ }), "shrink-0 group-data-[spacing=0]/toggle-group:rounded-none group-data-[spacing=0]/toggle-group:shadow-none focus:z-10 focus-visible:z-10 group-data-horizontal/toggle-group:data-[spacing=0]:first:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:first:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:last:rounded-none group-data-vertical/toggle-group:data-[spacing=0]:last:rounded-none group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:border-l-0 group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:border-t-0 group-data-horizontal/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-l group-data-vertical/toggle-group:data-[spacing=0]:data-[variant=outline]:first:border-t", className), ...props, children: children }));
23
23
  }
24
24
  export { ToggleGroup, ToggleGroupItem };
package/dist/toggle.d.ts CHANGED
@@ -1,9 +1,19 @@
1
1
  import * as React from "react";
2
2
  import { type VariantProps } from "class-variance-authority";
3
3
  import { Toggle as TogglePrimitive } from "radix-ui";
4
+ /**
5
+ * Selected chrome shared by standalone Toggle and ToggleGroupItem.
6
+ *
7
+ * Radix Toggle sets `data-state=on` and `aria-pressed`. ToggleGroup items
8
+ * always set `data-state=on`; in multiple mode they also set `aria-pressed`,
9
+ * but in single mode they are radios with `aria-checked` and no
10
+ * `aria-pressed`. All three selectors apply the same wash, text, and
11
+ * selected-foreground border so the state meets WCAG 1.4.11 in every mode.
12
+ */
13
+ declare const selectedToggleChrome = "data-on:border-selected-foreground data-on:bg-selected data-on:text-selected-foreground aria-pressed:border-selected-foreground aria-pressed:bg-selected aria-pressed:text-selected-foreground aria-checked:border-selected-foreground aria-checked:bg-selected aria-checked:text-selected-foreground";
4
14
  declare const toggleVariants: (props?: ({
5
15
  variant?: "default" | "outline" | null | undefined;
6
16
  size?: "default" | "sm" | "lg" | null | undefined;
7
17
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
8
18
  declare function Toggle({ className, variant, size, ...props }: React.ComponentProps<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants>): React.JSX.Element;
9
- export { Toggle, toggleVariants };
19
+ export { Toggle, toggleVariants, selectedToggleChrome };
package/dist/toggle.js CHANGED
@@ -4,10 +4,20 @@ import * as React from "react";
4
4
  import { cva } from "class-variance-authority";
5
5
  import { Toggle as TogglePrimitive } from "radix-ui";
6
6
  import { cn } from "./utils.js";
7
- const toggleVariants = cva("group/toggle inline-flex items-center justify-center gap-1.5 rounded-none text-xs font-semibold tracking-widest whitespace-nowrap uppercase transition-colors outline-none hover:border-primary hover:bg-selected hover:text-primary focus-visible:border-primary focus-visible:text-primary focus-visible:ring-[3px] focus-visible:ring-primary/30 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 aria-pressed:bg-selected aria-pressed:text-selected-foreground dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5", {
7
+ /**
8
+ * Selected chrome shared by standalone Toggle and ToggleGroupItem.
9
+ *
10
+ * Radix Toggle sets `data-state=on` and `aria-pressed`. ToggleGroup items
11
+ * always set `data-state=on`; in multiple mode they also set `aria-pressed`,
12
+ * but in single mode they are radios with `aria-checked` and no
13
+ * `aria-pressed`. All three selectors apply the same wash, text, and
14
+ * selected-foreground border so the state meets WCAG 1.4.11 in every mode.
15
+ */
16
+ const selectedToggleChrome = "data-on:border-selected-foreground data-on:bg-selected data-on:text-selected-foreground aria-pressed:border-selected-foreground aria-pressed:bg-selected aria-pressed:text-selected-foreground aria-checked:border-selected-foreground aria-checked:bg-selected aria-checked:text-selected-foreground";
17
+ const toggleVariants = cva(`group/toggle inline-flex items-center justify-center gap-1.5 rounded-none text-xs font-semibold tracking-widest whitespace-nowrap uppercase transition-colors outline-none hover:border-selected-foreground hover:bg-selected hover:text-selected-foreground focus-visible:border-ring focus-visible:text-selected-foreground focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 ${selectedToggleChrome} [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5`, {
8
18
  variants: {
9
19
  variant: {
10
- default: "bg-transparent",
20
+ default: "border border-transparent bg-transparent",
11
21
  outline: "border border-input bg-transparent",
12
22
  },
13
23
  size: {
@@ -24,4 +34,4 @@ const toggleVariants = cva("group/toggle inline-flex items-center justify-center
24
34
  function Toggle({ className, variant = "default", size = "default", ...props }) {
25
35
  return (_jsx(TogglePrimitive.Root, { "data-slot": "toggle", className: cn(toggleVariants({ variant, size, className })), ...props }));
26
36
  }
27
- export { Toggle, toggleVariants };
37
+ export { Toggle, toggleVariants, selectedToggleChrome };
@@ -0,0 +1,15 @@
1
+ import * as React from "react";
2
+ import { Tooltip as TooltipPrimitive } from "radix-ui";
3
+ /**
4
+ * Shares open delay across tooltips. `Tooltip` adds one per instance, so a
5
+ * provider at the app root is optional; add it to tune `delayDuration`.
6
+ */
7
+ declare function TooltipProvider({ delayDuration, ...props }: React.ComponentProps<typeof TooltipPrimitive.Provider>): React.JSX.Element;
8
+ /**
9
+ * Short label for an icon button or truncated text. Opens on hover and
10
+ * keyboard focus; put essential information in visible text instead.
11
+ */
12
+ declare function Tooltip({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Root>): React.JSX.Element;
13
+ declare function TooltipTrigger({ ...props }: React.ComponentProps<typeof TooltipPrimitive.Trigger>): React.JSX.Element;
14
+ declare function TooltipContent({ className, sideOffset, children, ...props }: React.ComponentProps<typeof TooltipPrimitive.Content>): React.JSX.Element;
15
+ export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
@@ -0,0 +1,26 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { Tooltip as TooltipPrimitive } from "radix-ui";
5
+ import { cn } from "./utils.js";
6
+ /**
7
+ * Shares open delay across tooltips. `Tooltip` adds one per instance, so a
8
+ * provider at the app root is optional; add it to tune `delayDuration`.
9
+ */
10
+ function TooltipProvider({ delayDuration = 0, ...props }) {
11
+ return (_jsx(TooltipPrimitive.Provider, { "data-slot": "tooltip-provider", delayDuration: delayDuration, ...props }));
12
+ }
13
+ /**
14
+ * Short label for an icon button or truncated text. Opens on hover and
15
+ * keyboard focus; put essential information in visible text instead.
16
+ */
17
+ function Tooltip({ ...props }) {
18
+ return (_jsx(TooltipProvider, { children: _jsx(TooltipPrimitive.Root, { "data-slot": "tooltip", ...props }) }));
19
+ }
20
+ function TooltipTrigger({ ...props }) {
21
+ return _jsx(TooltipPrimitive.Trigger, { "data-slot": "tooltip-trigger", ...props });
22
+ }
23
+ function TooltipContent({ className, sideOffset = 4, children, ...props }) {
24
+ return (_jsx(TooltipPrimitive.Portal, { children: _jsxs(TooltipPrimitive.Content, { "data-slot": "tooltip-content", sideOffset: sideOffset, className: cn("z-50 w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) rounded-none bg-foreground px-3 py-1.5 text-xs text-balance text-background duration-100 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 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-[state=instant-open]:animate-in data-[state=instant-open]:fade-in-0", className), ...props, children: [children, _jsx(TooltipPrimitive.Arrow, { className: "fill-foreground" })] }) }));
25
+ }
26
+ export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };