@ashwa-io/ui 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/components/atomic/Button/buttonLook.js +18 -0
  2. package/dist/components/atomic/Tooltip/Tooltip.d.ts +14 -0
  3. package/dist/components/atomic/Tooltip/Tooltip.js +14 -0
  4. package/dist/components/atomic/Tooltip/index.d.ts +2 -0
  5. package/dist/components/molecule/ConfirmDialog/ConfirmDialog.d.ts +17 -0
  6. package/dist/components/molecule/ConfirmDialog/ConfirmDialog.js +28 -0
  7. package/dist/components/molecule/ConfirmDialog/index.d.ts +2 -0
  8. package/dist/components/molecule/DropdownMenu/DropdownMenu.d.ts +28 -0
  9. package/dist/components/molecule/DropdownMenu/DropdownMenu.js +53 -0
  10. package/dist/components/molecule/DropdownMenu/index.d.ts +2 -0
  11. package/dist/components/molecule/Modal/Modal.d.ts +22 -0
  12. package/dist/components/molecule/Modal/Modal.js +48 -0
  13. package/dist/components/molecule/Modal/index.d.ts +2 -0
  14. package/dist/components/molecule/Popover/Popover.d.ts +24 -0
  15. package/dist/components/molecule/Popover/Popover.js +38 -0
  16. package/dist/components/molecule/Popover/index.d.ts +2 -0
  17. package/dist/components/molecule/Toaster/Toaster.d.ts +3 -0
  18. package/dist/components/molecule/Toaster/Toaster.js +19 -0
  19. package/dist/components/molecule/Toaster/index.d.ts +3 -0
  20. package/dist/components/molecule/Toaster/toast.d.ts +13 -0
  21. package/dist/components/molecule/Toaster/toast.js +18 -0
  22. package/dist/components/ui/alert-dialog.js +61 -0
  23. package/dist/components/ui/dialog.js +78 -0
  24. package/dist/components/ui/dropdown-menu.js +42 -0
  25. package/dist/components/ui/popover.js +32 -0
  26. package/dist/components/ui/tooltip.js +39 -0
  27. package/dist/index.d.ts +15 -2
  28. package/dist/index.js +8 -1
  29. package/dist/lib/types.d.ts +4 -0
  30. package/dist/theme/brandTheme.d.ts +1 -1
  31. package/dist/theme/brandTheme.js +10 -8
  32. package/package.json +2 -1
  33. package/src/styles/index.css +45 -43
@@ -0,0 +1,18 @@
1
+ import { BUTTON_VARIANT } from "./buttonVariant.js";
2
+ // src/components/atomic/Button/buttonLook.ts
3
+ const TEXT_SIZE = {
4
+ sm: "sm",
5
+ md: "default",
6
+ lg: "lg"
7
+ };
8
+ const ICON_SIZE = {
9
+ sm: "icon-sm",
10
+ md: "icon",
11
+ lg: "icon-lg"
12
+ };
13
+ /** Primitive props for an overlay trigger drawn in the public Button look. */
14
+ const buttonLook = (variant, size, iconOnly) => ({
15
+ variant: BUTTON_VARIANT[variant],
16
+ size: (iconOnly ? ICON_SIZE : TEXT_SIZE)[size]
17
+ });
18
+ export { buttonLook };
@@ -0,0 +1,14 @@
1
+ import { OverlaySide } from "../../../lib/types.js";
2
+ import React from "react";
3
+ export interface TooltipProps {
4
+ /**
5
+ * Short plain text, a visual hint only: screen readers don't get it, and it needs a focusable
6
+ * child to appear for keyboards. Never put essential information only here.
7
+ */
8
+ content: string;
9
+ children: React.ReactNode;
10
+ side?: OverlaySide;
11
+ /** Lets a full-width child (e.g. `<Button fullWidth>`) fill its container. */
12
+ fullWidth?: boolean;
13
+ }
14
+ export declare const Tooltip: React.FC<TooltipProps>;
@@ -0,0 +1,14 @@
1
+ import { Tooltip as Tooltip$1, TooltipContent, TooltipProvider, TooltipTrigger } from "../../ui/tooltip.js";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ // src/components/atomic/Tooltip/Tooltip.tsx
4
+ const Tooltip = (props) => {
5
+ const { content, children, side = "top", fullWidth = false } = props;
6
+ return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip$1, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
7
+ render: /* @__PURE__ */ jsx("span", { className: fullWidth ? "flex w-full" : "inline-flex max-w-full min-w-0" }),
8
+ children
9
+ }), /* @__PURE__ */ jsx(TooltipContent, {
10
+ side,
11
+ children: content
12
+ })] }) });
13
+ };
14
+ export { Tooltip };
@@ -0,0 +1,2 @@
1
+ import { Tooltip, TooltipProps } from "./Tooltip.js";
2
+ export { Tooltip, type TooltipProps };
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+ export interface ConfirmDialogProps {
3
+ open: boolean;
4
+ /** Cancel and Escape. */
5
+ onClose: () => void;
6
+ /** Never closes the dialog: the app closes it when its work finishes. */
7
+ onConfirm: () => void;
8
+ title: string;
9
+ description?: string;
10
+ confirmLabel: string;
11
+ cancelLabel?: string;
12
+ destructive?: boolean;
13
+ /** Confirm shows busy, Cancel is disabled and Escape is ignored. */
14
+ loading?: boolean;
15
+ 'data-testid'?: string;
16
+ }
17
+ export declare const ConfirmDialog: React.FC<ConfirmDialogProps>;
@@ -0,0 +1,28 @@
1
+ import { Button } from "../../atomic/Button/Button.js";
2
+ import { AlertDialog, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "../../ui/alert-dialog.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ // src/components/molecule/ConfirmDialog/ConfirmDialog.tsx
5
+ const ConfirmDialog = (props) => {
6
+ const { open, onClose, onConfirm, title, description, confirmLabel, cancelLabel = "Cancel", destructive = false, loading = false, "data-testid": testId } = props;
7
+ return /* @__PURE__ */ jsx(AlertDialog, {
8
+ open,
9
+ onOpenChange: (next) => {
10
+ if (!next && !loading) onClose();
11
+ },
12
+ children: /* @__PURE__ */ jsxs(AlertDialogContent, {
13
+ "data-testid": testId,
14
+ children: [/* @__PURE__ */ jsxs(AlertDialogHeader, { children: [/* @__PURE__ */ jsx(AlertDialogTitle, { children: title }), description && /* @__PURE__ */ jsx(AlertDialogDescription, { children: description })] }), /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [/* @__PURE__ */ jsx(Button, {
15
+ variant: "outline",
16
+ disabled: loading,
17
+ onClick: onClose,
18
+ children: cancelLabel
19
+ }), /* @__PURE__ */ jsx(Button, {
20
+ variant: destructive ? "destructive" : "primary",
21
+ loading,
22
+ onClick: onConfirm,
23
+ children: confirmLabel
24
+ })] })]
25
+ })
26
+ });
27
+ };
28
+ export { ConfirmDialog };
@@ -0,0 +1,2 @@
1
+ import { ConfirmDialog, ConfirmDialogProps } from "./ConfirmDialog.js";
2
+ export { ConfirmDialog, type ConfirmDialogProps };
@@ -0,0 +1,28 @@
1
+ import "../../atomic/Button/index.js";
2
+ import { ButtonVariant } from "../../atomic/Button/buttonVariant.js";
3
+ import { IconComponent, OverlayAlign, Size } from "../../../lib/types.js";
4
+ import React from "react";
5
+ export interface MenuItem {
6
+ label: string;
7
+ onSelect: () => void;
8
+ icon?: IconComponent;
9
+ /** Second line under the label. */
10
+ description?: string;
11
+ disabled?: boolean;
12
+ destructive?: boolean;
13
+ }
14
+ export interface DropdownMenuProps {
15
+ /** Trigger text (the trigger's aria-label when iconOnly). */
16
+ label: string;
17
+ items: MenuItem[];
18
+ icon?: IconComponent;
19
+ iconOnly?: boolean;
20
+ /** Trigger look. */
21
+ variant?: ButtonVariant;
22
+ size?: Size;
23
+ align?: OverlayAlign;
24
+ disabled?: boolean;
25
+ onOpenChange?: (open: boolean) => void;
26
+ 'data-testid'?: string;
27
+ }
28
+ export declare const DropdownMenu: React.FC<DropdownMenuProps>;
@@ -0,0 +1,53 @@
1
+ import { buttonLook } from "../../atomic/Button/buttonLook.js";
2
+ import { Button } from "../../ui/button.js";
3
+ import { DropdownMenu as DropdownMenu$1, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "../../ui/dropdown-menu.js";
4
+ import { cn } from "../../../lib/utils.js";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { ChevronDownIcon } from "lucide-react";
7
+ // src/components/molecule/DropdownMenu/DropdownMenu.tsx
8
+ const DropdownMenu = (props) => {
9
+ const { label, items, icon: Icon, iconOnly = false, variant = "outline", size = "md", align = "end", disabled = false, onOpenChange, "data-testid": testId } = props;
10
+ const look = buttonLook(variant, size, iconOnly);
11
+ return /* @__PURE__ */ jsxs(DropdownMenu$1, {
12
+ onOpenChange: (open) => onOpenChange?.(open),
13
+ children: [/* @__PURE__ */ jsxs(DropdownMenuTrigger, {
14
+ disabled,
15
+ "aria-label": iconOnly ? label : void 0,
16
+ "data-testid": testId,
17
+ render: /* @__PURE__ */ jsx(Button, {
18
+ variant: look.variant,
19
+ size: look.size
20
+ }),
21
+ children: [
22
+ Icon && /* @__PURE__ */ jsx(Icon, {
23
+ "data-icon": iconOnly ? void 0 : "inline-start",
24
+ "aria-hidden": true
25
+ }),
26
+ !iconOnly && label,
27
+ !iconOnly && /* @__PURE__ */ jsx(ChevronDownIcon, {
28
+ "data-icon": "inline-end",
29
+ "aria-hidden": true
30
+ })
31
+ ]
32
+ }), /* @__PURE__ */ jsx(DropdownMenuContent, {
33
+ align,
34
+ className: "w-auto max-w-80 min-w-48",
35
+ children: items.map((item) => {
36
+ const ItemIcon = item.icon;
37
+ return /* @__PURE__ */ jsxs(DropdownMenuItem, {
38
+ onClick: item.onSelect,
39
+ disabled: item.disabled,
40
+ variant: item.destructive ? "destructive" : "default",
41
+ children: [ItemIcon && /* @__PURE__ */ jsx(ItemIcon, { "aria-hidden": true }), /* @__PURE__ */ jsxs("span", {
42
+ className: "flex flex-col",
43
+ children: [/* @__PURE__ */ jsx("span", { children: item.label }), item.description && /* @__PURE__ */ jsx("span", {
44
+ className: cn("text-xs", !item.destructive && "text-muted-foreground"),
45
+ children: item.description
46
+ })]
47
+ })]
48
+ }, item.label);
49
+ })
50
+ })]
51
+ });
52
+ };
53
+ export { DropdownMenu };
@@ -0,0 +1,2 @@
1
+ import { DropdownMenu, DropdownMenuProps, MenuItem } from "./DropdownMenu.js";
2
+ export { DropdownMenu, type DropdownMenuProps, type MenuItem };
@@ -0,0 +1,22 @@
1
+ import React from "react";
2
+ export type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | '2xl';
3
+ export interface ModalProps {
4
+ open: boolean;
5
+ onClose: () => void;
6
+ /** Names the dialog (required). */
7
+ title: string;
8
+ description?: string;
9
+ /** Body. */
10
+ children: React.ReactNode;
11
+ /** e.g. Cancel + Save buttons, right-aligned. */
12
+ footer?: React.ReactNode;
13
+ size?: ModalSize;
14
+ /** Fixed height (min(40rem, 85dvh)): the body scrolls, header and footer stay put. */
15
+ fixedHeight?: boolean;
16
+ /** Body without padding, for lists and tables that run edge to edge. */
17
+ flush?: boolean;
18
+ /** false: no close button; Escape and outside clicks do nothing (blocking flows). */
19
+ closable?: boolean;
20
+ 'data-testid'?: string;
21
+ }
22
+ export declare const Modal: React.FC<ModalProps>;
@@ -0,0 +1,48 @@
1
+ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "../../ui/dialog.js";
2
+ import { cn } from "../../../lib/utils.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { useCallback, useState } from "react";
5
+ // src/components/molecule/Modal/Modal.tsx
6
+ const SIZE = {
7
+ sm: "sm:max-w-md",
8
+ md: "sm:max-w-lg",
9
+ lg: "sm:max-w-xl",
10
+ xl: "sm:max-w-2xl",
11
+ "2xl": "sm:max-w-3xl"
12
+ };
13
+ const FOCUSABLE = "a[href],button:not(:disabled),input:not(:disabled),select:not(:disabled),textarea:not(:disabled),[tabindex]:not([tabindex=\"-1\"])";
14
+ const Modal = (props) => {
15
+ const { open, onClose, title, description, children, footer, size = "md", fixedHeight = false, flush = false, closable = true, "data-testid": testId } = props;
16
+ const [needsTabStop, setNeedsTabStop] = useState(false);
17
+ const bodyRef = useCallback((body) => {
18
+ if (!body) return void 0;
19
+ const check = () => setNeedsTabStop(body.scrollHeight > body.clientHeight && !body.querySelector(FOCUSABLE));
20
+ const observer = new ResizeObserver(check);
21
+ observer.observe(body);
22
+ Array.from(body.children).forEach((child) => observer.observe(child));
23
+ return () => observer.disconnect();
24
+ }, []);
25
+ return /* @__PURE__ */ jsx(Dialog, {
26
+ open,
27
+ onOpenChange: (next) => {
28
+ if (!next && closable) onClose();
29
+ },
30
+ children: /* @__PURE__ */ jsxs(DialogContent, {
31
+ showCloseButton: closable,
32
+ "data-testid": testId,
33
+ className: cn("flex max-h-[85dvh] flex-col", SIZE[size], fixedHeight && "h-[min(40rem,85dvh)]"),
34
+ children: [
35
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [/* @__PURE__ */ jsx(DialogTitle, { children: title }), description && /* @__PURE__ */ jsx(DialogDescription, { children: description })] }),
36
+ /* @__PURE__ */ jsx("div", {
37
+ ref: bodyRef,
38
+ "data-slot": "modal-body",
39
+ tabIndex: needsTabStop ? 0 : void 0,
40
+ className: cn("min-h-0 flex-1 overflow-y-auto", flush ? "-m-4" : "-m-1 p-1"),
41
+ children
42
+ }),
43
+ footer && /* @__PURE__ */ jsx(DialogFooter, { children: footer })
44
+ ]
45
+ })
46
+ });
47
+ };
48
+ export { Modal };
@@ -0,0 +1,2 @@
1
+ import { Modal, ModalProps, ModalSize } from "./Modal.js";
2
+ export { Modal, type ModalProps, type ModalSize };
@@ -0,0 +1,24 @@
1
+ import "../../atomic/Button/index.js";
2
+ import { ButtonVariant } from "../../atomic/Button/buttonVariant.js";
3
+ import { IconComponent, OverlayAlign, OverlaySide, Size } from "../../../lib/types.js";
4
+ import React from "react";
5
+ export type PopoverWidth = 'sm' | 'md' | 'lg';
6
+ export interface PopoverProps {
7
+ /** Trigger text (the trigger's aria-label when iconOnly); also names the popup. */
8
+ label: string;
9
+ icon?: IconComponent;
10
+ iconOnly?: boolean;
11
+ /** Trigger look. */
12
+ variant?: ButtonVariant;
13
+ size?: Size;
14
+ children: React.ReactNode;
15
+ /** Pass with onOpenChange to control it; otherwise it manages itself. */
16
+ open?: boolean;
17
+ onOpenChange?: (open: boolean) => void;
18
+ side?: OverlaySide;
19
+ align?: OverlayAlign;
20
+ width?: PopoverWidth;
21
+ disabled?: boolean;
22
+ 'data-testid'?: string;
23
+ }
24
+ export declare const Popover: React.FC<PopoverProps>;
@@ -0,0 +1,38 @@
1
+ import { buttonLook } from "../../atomic/Button/buttonLook.js";
2
+ import { Button } from "../../ui/button.js";
3
+ import { Popover as Popover$1, PopoverContent, PopoverTrigger } from "../../ui/popover.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ // src/components/molecule/Popover/Popover.tsx
6
+ const WIDTH = {
7
+ sm: "w-56",
8
+ md: "w-72",
9
+ lg: "w-96"
10
+ };
11
+ const Popover = (props) => {
12
+ const { label, icon: Icon, iconOnly = false, variant = "outline", size = "md", children, open, onOpenChange, side = "bottom", align = "start", width = "md", disabled = false, "data-testid": testId } = props;
13
+ const look = buttonLook(variant, size, iconOnly);
14
+ return /* @__PURE__ */ jsxs(Popover$1, {
15
+ open,
16
+ onOpenChange: (next) => onOpenChange?.(next),
17
+ children: [/* @__PURE__ */ jsxs(PopoverTrigger, {
18
+ disabled,
19
+ "aria-label": iconOnly ? label : void 0,
20
+ "data-testid": testId,
21
+ render: /* @__PURE__ */ jsx(Button, {
22
+ variant: look.variant,
23
+ size: look.size
24
+ }),
25
+ children: [Icon && /* @__PURE__ */ jsx(Icon, {
26
+ "data-icon": iconOnly ? void 0 : "inline-start",
27
+ "aria-hidden": true
28
+ }), !iconOnly && label]
29
+ }), /* @__PURE__ */ jsx(PopoverContent, {
30
+ side,
31
+ align,
32
+ "aria-label": label,
33
+ className: WIDTH[width],
34
+ children
35
+ })]
36
+ });
37
+ };
38
+ export { Popover };
@@ -0,0 +1,2 @@
1
+ import { Popover, PopoverProps, PopoverWidth } from "./Popover.js";
2
+ export { Popover, type PopoverProps, type PopoverWidth };
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ /** Mount once at the app root. */
3
+ export declare const Toaster: React.FC;
@@ -0,0 +1,19 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { Toaster } from "sonner";
3
+ // src/components/molecule/Toaster/Toaster.tsx
4
+ const TOKENS = {
5
+ "--normal-bg": "var(--popover)",
6
+ "--normal-text": "var(--popover-foreground)",
7
+ "--normal-border": "var(--border)",
8
+ "--border-radius": "var(--radius)"
9
+ };
10
+ /** Mount once at the app root. */
11
+ const Toaster$1 = () => /* @__PURE__ */ jsx(Toaster, {
12
+ position: "bottom-right",
13
+ style: TOKENS,
14
+ toastOptions: { classNames: {
15
+ toast: "font-sans",
16
+ description: "text-muted-foreground!"
17
+ } }
18
+ });
19
+ export { Toaster$1 as Toaster };
@@ -0,0 +1,3 @@
1
+ import { ToastOptions, toast } from "./toast.js";
2
+ import { Toaster } from "./Toaster.js";
3
+ export { type ToastOptions, Toaster, toast };
@@ -0,0 +1,13 @@
1
+ export interface ToastOptions {
2
+ description?: string;
3
+ duration?: number;
4
+ }
5
+ /** Works anywhere, including outside React (axios interceptors); needs one mounted <Toaster />. */
6
+ export declare const toast: {
7
+ message: (message: string, options?: ToastOptions) => string | number;
8
+ success: (message: string, options?: ToastOptions) => string | number;
9
+ error: (message: string, options?: ToastOptions) => string | number;
10
+ info: (message: string, options?: ToastOptions) => string | number;
11
+ warning: (message: string, options?: ToastOptions) => string | number;
12
+ dismiss: (id?: string | number) => void;
13
+ };
@@ -0,0 +1,18 @@
1
+ import { toast } from "sonner";
2
+ // src/components/molecule/Toaster/toast.ts
3
+ const show = (notify) => (message, options) => notify(message, {
4
+ description: options?.description,
5
+ duration: options?.duration
6
+ });
7
+ /** Works anywhere, including outside React (axios interceptors); needs one mounted <Toaster />. */
8
+ const toast$1 = {
9
+ message: show(toast.message),
10
+ success: show(toast.success),
11
+ error: show(toast.error),
12
+ info: show(toast.info),
13
+ warning: show(toast.warning),
14
+ dismiss: (id) => {
15
+ toast.dismiss(id);
16
+ }
17
+ };
18
+ export { toast$1 as toast };
@@ -0,0 +1,61 @@
1
+ import { cn } from "cn";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import "react";
4
+ import { AlertDialog } from "@base-ui/react/alert-dialog";
5
+ // src/components/ui/alert-dialog.tsx
6
+ const AlertDialog$1 = ({ ...props }) => {
7
+ return /* @__PURE__ */ jsx(AlertDialog.Root, {
8
+ "data-slot": "alert-dialog",
9
+ ...props
10
+ });
11
+ };
12
+ const AlertDialogPortal = ({ ...props }) => {
13
+ return /* @__PURE__ */ jsx(AlertDialog.Portal, {
14
+ "data-slot": "alert-dialog-portal",
15
+ ...props
16
+ });
17
+ };
18
+ const AlertDialogOverlay = ({ className, ...props }) => {
19
+ return /* @__PURE__ */ jsx(AlertDialog.Backdrop, {
20
+ "data-slot": "alert-dialog-overlay",
21
+ className: cn("data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0 fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs", className),
22
+ ...props
23
+ });
24
+ };
25
+ const AlertDialogContent = ({ className, size = "default", ...props }) => {
26
+ return /* @__PURE__ */ jsxs(AlertDialogPortal, { children: [/* @__PURE__ */ jsx(AlertDialogOverlay, {}), /* @__PURE__ */ jsx(AlertDialog.Popup, {
27
+ "data-slot": "alert-dialog-content",
28
+ "data-size": size,
29
+ className: cn("group/alert-dialog-content bg-popover text-popover-foreground ring-foreground/10 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 fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl p-4 ring-1 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm", className),
30
+ ...props
31
+ })] });
32
+ };
33
+ const AlertDialogHeader = ({ className, ...props }) => {
34
+ return /* @__PURE__ */ jsx("div", {
35
+ "data-slot": "alert-dialog-header",
36
+ className: cn("grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]", className),
37
+ ...props
38
+ });
39
+ };
40
+ const AlertDialogFooter = ({ className, ...props }) => {
41
+ return /* @__PURE__ */ jsx("div", {
42
+ "data-slot": "alert-dialog-footer",
43
+ className: cn("bg-muted/50 -mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t p-4 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end", className),
44
+ ...props
45
+ });
46
+ };
47
+ const AlertDialogTitle = ({ className, ...props }) => {
48
+ return /* @__PURE__ */ jsx(AlertDialog.Title, {
49
+ "data-slot": "alert-dialog-title",
50
+ className: cn("font-heading text-base font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2", className),
51
+ ...props
52
+ });
53
+ };
54
+ const AlertDialogDescription = ({ className, ...props }) => {
55
+ return /* @__PURE__ */ jsx(AlertDialog.Description, {
56
+ "data-slot": "alert-dialog-description",
57
+ className: cn("text-muted-foreground *:[a]:hover:text-foreground text-sm text-balance md:text-pretty *:[a]:underline *:[a]:underline-offset-3", className),
58
+ ...props
59
+ });
60
+ };
61
+ export { AlertDialog$1 as AlertDialog, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogOverlay, AlertDialogPortal, AlertDialogTitle };
@@ -0,0 +1,78 @@
1
+ import { Button } from "./button.js";
2
+ import { cn } from "cn";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ import { XIcon } from "lucide-react";
5
+ import "react";
6
+ import { Dialog } from "@base-ui/react/dialog";
7
+ // src/components/ui/dialog.tsx
8
+ const Dialog$1 = ({ ...props }) => {
9
+ return /* @__PURE__ */ jsx(Dialog.Root, {
10
+ "data-slot": "dialog",
11
+ ...props
12
+ });
13
+ };
14
+ const DialogPortal = ({ ...props }) => {
15
+ return /* @__PURE__ */ jsx(Dialog.Portal, {
16
+ "data-slot": "dialog-portal",
17
+ ...props
18
+ });
19
+ };
20
+ const DialogOverlay = ({ className, ...props }) => {
21
+ return /* @__PURE__ */ jsx(Dialog.Backdrop, {
22
+ "data-slot": "dialog-overlay",
23
+ className: cn("data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0 fixed inset-0 isolate z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs", className),
24
+ ...props
25
+ });
26
+ };
27
+ const DialogContent = ({ className, children, showCloseButton = true, ...props }) => {
28
+ return /* @__PURE__ */ jsxs(DialogPortal, { children: [/* @__PURE__ */ jsx(DialogOverlay, {}), /* @__PURE__ */ jsxs(Dialog.Popup, {
29
+ "data-slot": "dialog-content",
30
+ className: cn("bg-popover text-popover-foreground ring-foreground/10 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 fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl p-4 text-sm ring-1 duration-100 outline-none sm:max-w-sm", className),
31
+ ...props,
32
+ children: [children, showCloseButton && /* @__PURE__ */ jsxs(Dialog.Close, {
33
+ "data-slot": "dialog-close",
34
+ render: /* @__PURE__ */ jsx(Button, {
35
+ variant: "ghost",
36
+ className: "absolute top-2 right-2",
37
+ size: "icon-sm"
38
+ }),
39
+ children: [/* @__PURE__ */ jsx(XIcon, {}), /* @__PURE__ */ jsx("span", {
40
+ className: "sr-only",
41
+ children: "Close"
42
+ })]
43
+ })]
44
+ })] });
45
+ };
46
+ const DialogHeader = ({ className, ...props }) => {
47
+ return /* @__PURE__ */ jsx("div", {
48
+ "data-slot": "dialog-header",
49
+ className: cn("flex flex-col gap-2", className),
50
+ ...props
51
+ });
52
+ };
53
+ const DialogFooter = ({ className, showCloseButton = false, children, ...props }) => {
54
+ return /* @__PURE__ */ jsxs("div", {
55
+ "data-slot": "dialog-footer",
56
+ className: cn("bg-muted/50 -mx-4 -mb-4 flex flex-col-reverse gap-2 rounded-b-xl border-t p-4 sm:flex-row sm:justify-end", className),
57
+ ...props,
58
+ children: [children, showCloseButton && /* @__PURE__ */ jsx(Dialog.Close, {
59
+ render: /* @__PURE__ */ jsx(Button, { variant: "outline" }),
60
+ children: "Close"
61
+ })]
62
+ });
63
+ };
64
+ const DialogTitle = ({ className, ...props }) => {
65
+ return /* @__PURE__ */ jsx(Dialog.Title, {
66
+ "data-slot": "dialog-title",
67
+ className: cn("font-heading text-base leading-none font-medium", className),
68
+ ...props
69
+ });
70
+ };
71
+ const DialogDescription = ({ className, ...props }) => {
72
+ return /* @__PURE__ */ jsx(Dialog.Description, {
73
+ "data-slot": "dialog-description",
74
+ className: cn("text-muted-foreground *:[a]:hover:text-foreground text-sm *:[a]:underline *:[a]:underline-offset-3", className),
75
+ ...props
76
+ });
77
+ };
78
+ export { Dialog$1 as Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle };
@@ -0,0 +1,42 @@
1
+ import { cn } from "cn";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import "lucide-react";
4
+ import "react";
5
+ import { Menu } from "@base-ui/react/menu";
6
+ // src/components/ui/dropdown-menu.tsx
7
+ const DropdownMenu = ({ ...props }) => {
8
+ return /* @__PURE__ */ jsx(Menu.Root, {
9
+ "data-slot": "dropdown-menu",
10
+ ...props
11
+ });
12
+ };
13
+ const DropdownMenuTrigger = ({ ...props }) => {
14
+ return /* @__PURE__ */ jsx(Menu.Trigger, {
15
+ "data-slot": "dropdown-menu-trigger",
16
+ ...props
17
+ });
18
+ };
19
+ const DropdownMenuContent = ({ align = "start", alignOffset = 0, side = "bottom", sideOffset = 4, className, ...props }) => {
20
+ return /* @__PURE__ */ jsx(Menu.Portal, { children: /* @__PURE__ */ jsx(Menu.Positioner, {
21
+ className: "isolate z-50 outline-none",
22
+ align,
23
+ alignOffset,
24
+ side,
25
+ sideOffset,
26
+ children: /* @__PURE__ */ jsx(Menu.Popup, {
27
+ "data-slot": "dropdown-menu-content",
28
+ className: cn("bg-popover text-popover-foreground ring-foreground/10 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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 z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg p-1 shadow-md ring-1 duration-100 outline-none data-closed:overflow-hidden", className),
29
+ ...props
30
+ })
31
+ }) });
32
+ };
33
+ const DropdownMenuItem = ({ className, inset, variant = "default", ...props }) => {
34
+ return /* @__PURE__ */ jsx(Menu.Item, {
35
+ "data-slot": "dropdown-menu-item",
36
+ "data-inset": inset,
37
+ "data-variant": variant,
38
+ className: cn("group/dropdown-menu-item focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:*:[svg]:text-destructive relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 data-inset:pl-7 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", className),
39
+ ...props
40
+ });
41
+ };
42
+ export { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger };
@@ -0,0 +1,32 @@
1
+ import { cn } from "cn";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import "react";
4
+ import { Popover } from "@base-ui/react/popover";
5
+ // src/components/ui/popover.tsx
6
+ const Popover$1 = ({ ...props }) => {
7
+ return /* @__PURE__ */ jsx(Popover.Root, {
8
+ "data-slot": "popover",
9
+ ...props
10
+ });
11
+ };
12
+ const PopoverTrigger = ({ ...props }) => {
13
+ return /* @__PURE__ */ jsx(Popover.Trigger, {
14
+ "data-slot": "popover-trigger",
15
+ ...props
16
+ });
17
+ };
18
+ const PopoverContent = ({ className, align = "center", alignOffset = 0, side = "bottom", sideOffset = 4, ...props }) => {
19
+ return /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsx(Popover.Positioner, {
20
+ align,
21
+ alignOffset,
22
+ side,
23
+ sideOffset,
24
+ className: "isolate z-50",
25
+ children: /* @__PURE__ */ jsx(Popover.Popup, {
26
+ "data-slot": "popover-content",
27
+ className: cn("bg-popover text-popover-foreground ring-foreground/10 data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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 z-50 flex w-72 origin-(--transform-origin) flex-col gap-2.5 rounded-lg p-2.5 text-sm shadow-md ring-1 outline-hidden duration-100", className),
28
+ ...props
29
+ })
30
+ }) });
31
+ };
32
+ export { Popover$1 as Popover, PopoverContent, PopoverTrigger };
@@ -0,0 +1,39 @@
1
+ import { cn } from "cn";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { Tooltip } from "@base-ui/react/tooltip";
4
+ // src/components/ui/tooltip.tsx
5
+ const TooltipProvider = ({ delay = 0, ...props }) => {
6
+ return /* @__PURE__ */ jsx(Tooltip.Provider, {
7
+ "data-slot": "tooltip-provider",
8
+ delay,
9
+ ...props
10
+ });
11
+ };
12
+ const Tooltip$1 = ({ ...props }) => {
13
+ return /* @__PURE__ */ jsx(Tooltip.Root, {
14
+ "data-slot": "tooltip",
15
+ ...props
16
+ });
17
+ };
18
+ const TooltipTrigger = ({ ...props }) => {
19
+ return /* @__PURE__ */ jsx(Tooltip.Trigger, {
20
+ "data-slot": "tooltip-trigger",
21
+ ...props
22
+ });
23
+ };
24
+ const TooltipContent = ({ className, side = "top", sideOffset = 4, align = "center", alignOffset = 0, children, ...props }) => {
25
+ return /* @__PURE__ */ jsx(Tooltip.Portal, { children: /* @__PURE__ */ jsx(Tooltip.Positioner, {
26
+ align,
27
+ alignOffset,
28
+ side,
29
+ sideOffset,
30
+ className: "isolate z-50",
31
+ children: /* @__PURE__ */ jsxs(Tooltip.Popup, {
32
+ "data-slot": "tooltip-content",
33
+ className: cn("bg-foreground text-background data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-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=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 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 z-50 inline-flex w-fit max-w-xs origin-(--transform-origin) items-center gap-1.5 rounded-md px-3 py-1.5 text-xs has-data-[slot=kbd]:pr-1.5 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm", className),
34
+ ...props,
35
+ children: [children, /* @__PURE__ */ jsx(Tooltip.Arrow, { className: "bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%-2px)] rotate-45 rounded-[2px] data-[side=bottom]:top-1 data-[side=inline-end]:top-1/2! data-[side=inline-end]:-left-1 data-[side=inline-end]:-translate-y-1/2 data-[side=inline-start]:top-1/2! data-[side=inline-start]:-right-1 data-[side=inline-start]:-translate-y-1/2 data-[side=left]:top-1/2! data-[side=left]:-right-1 data-[side=left]:-translate-y-1/2 data-[side=right]:top-1/2! data-[side=right]:-left-1 data-[side=right]:-translate-y-1/2 data-[side=top]:-bottom-2.5" })]
36
+ })
37
+ }) });
38
+ };
39
+ export { Tooltip$1 as Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
package/dist/index.d.ts CHANGED
@@ -19,6 +19,8 @@ import "./components/atomic/TextArea/index.js";
19
19
  import { TextArea, TextAreaProps } from "./components/atomic/TextArea/TextArea.js";
20
20
  import "./components/atomic/TextInput/index.js";
21
21
  import { TextInput, TextInputProps, TextInputType } from "./components/atomic/TextInput/TextInput.js";
22
+ import "./components/atomic/Tooltip/index.js";
23
+ import { Tooltip, TooltipProps } from "./components/atomic/Tooltip/Tooltip.js";
22
24
  import "./components/atomic/IconButton/index.js";
23
25
  import { IconButton, IconButtonProps, IconButtonVariant } from "./components/atomic/IconButton/IconButton.js";
24
26
  import "./components/atomic/Select/index.js";
@@ -29,9 +31,20 @@ import "./components/atomic/Spinner/index.js";
29
31
  import { Spinner, SpinnerProps } from "./components/atomic/Spinner/Spinner.js";
30
32
  import "./components/layout/FormLayout/index.js";
31
33
  import { FormLayout, FormLayoutDirection, FormLayoutProps } from "./components/layout/FormLayout/FormLayout.js";
34
+ import "./components/molecule/ConfirmDialog/index.js";
35
+ import { ConfirmDialog, ConfirmDialogProps } from "./components/molecule/ConfirmDialog/ConfirmDialog.js";
36
+ import "./components/molecule/DropdownMenu/index.js";
37
+ import { DropdownMenu, DropdownMenuProps, MenuItem } from "./components/molecule/DropdownMenu/DropdownMenu.js";
32
38
  import "./components/molecule/FormField/index.js";
33
39
  import { FormField, FormFieldControlProps, FormFieldProps } from "./components/molecule/FormField/FormField.js";
40
+ import "./components/molecule/Modal/index.js";
41
+ import { Modal, ModalProps, ModalSize } from "./components/molecule/Modal/Modal.js";
42
+ import "./components/molecule/Popover/index.js";
43
+ import { Popover, PopoverProps, PopoverWidth } from "./components/molecule/Popover/Popover.js";
44
+ import "./components/molecule/Toaster/index.js";
45
+ import { ToastOptions, toast } from "./components/molecule/Toaster/toast.js";
46
+ import { Toaster } from "./components/molecule/Toaster/Toaster.js";
34
47
  import "./components/layout/Stack/index.js";
35
48
  import { HStack, HStackProps, Stack, StackAlign, StackGap, StackJustify, StackProps, VStack, VStackProps } from "./components/layout/Stack/Stack.js";
36
- import { FieldBaseProps, IconComponent, Size, Tone } from "./lib/types.js";
37
- export { Badge, type BadgeProps, Button, type ButtonProps, type ButtonVariant, Checkbox, type CheckboxProps, ColorInput, type ColorInputProps, Divider, type DividerProps, type FieldBaseProps, FormField, type FormFieldControlProps, type FormFieldProps, FormLayout, type FormLayoutDirection, type FormLayoutProps, HStack, type HStackProps, Heading, type HeadingLevel, type HeadingProps, IconButton, type IconButtonProps, type IconButtonVariant, type IconComponent, Select, type SelectOption, type SelectProps, type Size, Skeleton, type SkeletonProps, type SkeletonShape, Spinner, type SpinnerProps, Stack, type StackAlign, type StackGap, type StackJustify, type StackProps, Switch, type SwitchProps, Text, TextArea, type TextAreaProps, TextInput, type TextInputProps, type TextInputType, type TextProps, type TextSize, type TextTone, type TextWeight, type Tone, VStack, type VStackProps };
49
+ import { FieldBaseProps, IconComponent, OverlayAlign, OverlaySide, Size, Tone } from "./lib/types.js";
50
+ export { Badge, type BadgeProps, Button, type ButtonProps, type ButtonVariant, Checkbox, type CheckboxProps, ColorInput, type ColorInputProps, ConfirmDialog, type ConfirmDialogProps, Divider, type DividerProps, DropdownMenu, type DropdownMenuProps, type FieldBaseProps, FormField, type FormFieldControlProps, type FormFieldProps, FormLayout, type FormLayoutDirection, type FormLayoutProps, HStack, type HStackProps, Heading, type HeadingLevel, type HeadingProps, IconButton, type IconButtonProps, type IconButtonVariant, type IconComponent, type MenuItem, Modal, type ModalProps, type ModalSize, type OverlayAlign, type OverlaySide, Popover, type PopoverProps, type PopoverWidth, Select, type SelectOption, type SelectProps, type Size, Skeleton, type SkeletonProps, type SkeletonShape, Spinner, type SpinnerProps, Stack, type StackAlign, type StackGap, type StackJustify, type StackProps, Switch, type SwitchProps, Text, TextArea, type TextAreaProps, TextInput, type TextInputProps, type TextInputType, type TextProps, type TextSize, type TextTone, type TextWeight, type ToastOptions, Toaster, type Tone, Tooltip, type TooltipProps, VStack, type VStackProps, toast };
package/dist/index.js CHANGED
@@ -8,11 +8,18 @@ import { Switch } from "./components/atomic/Switch/Switch.js";
8
8
  import { Text } from "./components/atomic/Text/Text.js";
9
9
  import { TextArea } from "./components/atomic/TextArea/TextArea.js";
10
10
  import { TextInput } from "./components/atomic/TextInput/TextInput.js";
11
+ import { Tooltip } from "./components/atomic/Tooltip/Tooltip.js";
11
12
  import { IconButton } from "./components/atomic/IconButton/IconButton.js";
12
13
  import { Select } from "./components/atomic/Select/Select.js";
13
14
  import { Skeleton } from "./components/atomic/Skeleton/Skeleton.js";
14
15
  import { Spinner } from "./components/atomic/Spinner/Spinner.js";
15
16
  import { FormLayout } from "./components/layout/FormLayout/FormLayout.js";
17
+ import { ConfirmDialog } from "./components/molecule/ConfirmDialog/ConfirmDialog.js";
18
+ import { DropdownMenu } from "./components/molecule/DropdownMenu/DropdownMenu.js";
16
19
  import { FormField } from "./components/molecule/FormField/FormField.js";
20
+ import { Modal } from "./components/molecule/Modal/Modal.js";
21
+ import { Popover } from "./components/molecule/Popover/Popover.js";
22
+ import { toast } from "./components/molecule/Toaster/toast.js";
23
+ import { Toaster } from "./components/molecule/Toaster/Toaster.js";
17
24
  import { HStack, Stack, VStack } from "./components/layout/Stack/Stack.js";
18
- export { Badge, Button, Checkbox, ColorInput, Divider, FormField, FormLayout, HStack, Heading, IconButton, Select, Skeleton, Spinner, Stack, Switch, Text, TextArea, TextInput, VStack };
25
+ export { Badge, Button, Checkbox, ColorInput, ConfirmDialog, Divider, DropdownMenu, FormField, FormLayout, HStack, Heading, IconButton, Modal, Popover, Select, Skeleton, Spinner, Stack, Switch, Text, TextArea, TextInput, Toaster, Tooltip, VStack, toast };
@@ -2,6 +2,10 @@ import React from "react";
2
2
  /** Shared size scale for every public component. */
3
3
  export type Size = 'sm' | 'md' | 'lg';
4
4
  export type Tone = 'neutral' | 'brand' | 'success' | 'warning' | 'danger' | 'info';
5
+ /** Which side of the trigger an overlay opens on. */
6
+ export type OverlaySide = 'top' | 'bottom' | 'left' | 'right';
7
+ /** How an overlay lines up with its trigger along that side. */
8
+ export type OverlayAlign = 'start' | 'center' | 'end';
5
9
  /** An icon component (lucide-react, svgr): the design system controls its size and colour. */
6
10
  export type IconComponent = React.ComponentType<{
7
11
  className?: string;
@@ -1,4 +1,4 @@
1
- export declare const DEFAULT_BRAND_HEX = "#8750f6";
1
+ export declare const DEFAULT_BRAND_HEX = "#8556ef";
2
2
  export type BrandWarning = 'adjusted' | 'near-destructive' | 'invalid';
3
3
  export interface BrandTokens {
4
4
  '--brand': string;
@@ -4,7 +4,7 @@ useMode(modeRgb);
4
4
  useMode(modeLrgb);
5
5
  useMode(modeOklab);
6
6
  useMode(modeOklch);
7
- const DEFAULT_BRAND_HEX = "#8750f6";
7
+ const DEFAULT_BRAND_HEX = "#8556ef";
8
8
  const SURFACES = {
9
9
  light: [{
10
10
  mode: "oklch",
@@ -13,12 +13,14 @@ const SURFACES = {
13
13
  }],
14
14
  dark: [{
15
15
  mode: "oklch",
16
- l: .145,
17
- c: 0
16
+ l: .141,
17
+ c: .005,
18
+ h: 285.823
18
19
  }, {
19
20
  mode: "oklch",
20
- l: .205,
21
- c: 0
21
+ l: .21,
22
+ c: .006,
23
+ h: 285.885
22
24
  }]
23
25
  };
24
26
  const DIRECTION = {
@@ -48,7 +50,7 @@ const BLACK = {
48
50
  c: 0
49
51
  };
50
52
  const MIN_UI_CONTRAST = 3;
51
- const MIN_TEXT_CONTRAST = 4.5;
53
+ const MIN_TEXT_CONTRAST = 4.55;
52
54
  const LIGHTNESS_STEP = .005;
53
55
  const MAX_STEPS = 200;
54
56
  const WHITE_LABEL_MAX_STEPS = 10;
@@ -137,10 +139,10 @@ const parseBrand = (hex) => {
137
139
  };
138
140
  /** Brand tokens for light and dark from one org hex. Never throws; bad input → default. */
139
141
  const brandTheme = (hex) => {
140
- const input = parseBrand(hex ?? "#8750f6");
142
+ const input = parseBrand(hex ?? "#8556ef");
141
143
  if (input) return buildTheme(input);
142
144
  return {
143
- ...buildTheme(parseBrand("#8750f6") ?? BLACK),
145
+ ...buildTheme(parseBrand("#8556ef") ?? BLACK),
144
146
  warnings: ["invalid"]
145
147
  };
146
148
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ashwa-io/ui",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Ashwa design system: locked React components on shadcn/ui (Base UI) and Tailwind v4, with per-org brand theming.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -102,6 +102,7 @@
102
102
  "class-variance-authority": "^0.7.1",
103
103
  "cn": "^0.5.2",
104
104
  "culori": "^4.0.2",
105
+ "sonner": "^2.0.8",
105
106
  "tw-animate-css": "^1.4.0"
106
107
  }
107
108
  }
@@ -64,27 +64,27 @@
64
64
  :root {
65
65
  /* Brand group: defaults = brandTheme(DEFAULT_BRAND_HEX). Per-org values come from
66
66
  brandThemeCss(hex) in @ashwa-io/ui/theme, injected at runtime, and outrank these. */
67
- --brand: oklch(0.585 0.233 293);
67
+ --brand: oklch(0.586 0.218 292.5);
68
68
  --brand-foreground: oklch(1 0 0);
69
- --brand-text: oklch(0.585 0.233 293);
70
- --brand-tint: oklch(0.965 0.018 293);
71
- --brand-tint-foreground: oklch(0.565 0.233 293);
72
- --brand-map: #8750f6;
69
+ --brand-text: oklch(0.586 0.218 292.5);
70
+ --brand-tint: oklch(0.965 0.018 292.5);
71
+ --brand-tint-foreground: oklch(0.561 0.218 292.5);
72
+ --brand-map: #8556ef;
73
73
 
74
74
  --background: oklch(1 0 0);
75
- --foreground: oklch(0.145 0 0);
75
+ --foreground: oklch(0.141 0.005 285.823);
76
76
  --card: oklch(1 0 0);
77
- --card-foreground: oklch(0.145 0 0);
77
+ --card-foreground: oklch(0.141 0.005 285.823);
78
78
  --popover: oklch(1 0 0);
79
- --popover-foreground: oklch(0.145 0 0);
79
+ --popover-foreground: oklch(0.141 0.005 285.823);
80
80
  --primary: var(--brand);
81
81
  --primary-foreground: var(--brand-foreground);
82
- --secondary: oklch(0.97 0 0);
83
- --secondary-foreground: oklch(0.205 0 0);
84
- --muted: oklch(0.97 0 0);
85
- --muted-foreground: oklch(0.556 0 0);
86
- --accent: oklch(0.97 0 0);
87
- --accent-foreground: oklch(0.205 0 0);
82
+ --secondary: oklch(0.967 0.001 286.375);
83
+ --secondary-foreground: oklch(0.21 0.006 285.885);
84
+ --muted: oklch(0.967 0.001 286.375);
85
+ --muted-foreground: oklch(0.552 0.016 285.938);
86
+ --accent: oklch(0.967 0.001 286.375);
87
+ --accent-foreground: oklch(0.21 0.006 285.885);
88
88
  /* Tailwind red-700, darker than base-nova's red-600: destructive text on its 10% tint
89
89
  (Button destructive) must reach WCAG AA 4.5:1; red-600 only reached 4:1. */
90
90
  --destructive: oklch(0.505 0.213 27.518);
@@ -93,58 +93,60 @@
93
93
  --success: oklch(0.448 0.119 151.328);
94
94
  --warning: oklch(0.473 0.137 46.201);
95
95
  --info: oklch(0.488 0.243 264.376);
96
- --border: oklch(0.922 0 0);
97
- --input: oklch(0.922 0 0);
96
+ --border: oklch(0.92 0.004 286.32);
97
+ --input: oklch(0.92 0.004 286.32);
98
98
  --ring: var(--brand);
99
99
  --chart-1: var(--brand);
100
- --chart-2: oklch(0.556 0 0);
101
- --chart-3: oklch(0.439 0 0);
102
- --chart-4: oklch(0.371 0 0);
103
- --chart-5: oklch(0.269 0 0);
100
+ --chart-2: oklch(0.6 0.118 184.704);
101
+ --chart-3: oklch(0.398 0.07 227.392);
102
+ --chart-4: oklch(0.828 0.189 84.429);
103
+ --chart-5: oklch(0.769 0.188 70.08);
104
104
  --radius: 0.5rem;
105
105
  --sidebar: oklch(0.985 0 0);
106
- --sidebar-foreground: oklch(0.145 0 0);
106
+ --sidebar-foreground: oklch(0.141 0.005 285.823);
107
107
  --sidebar-primary: var(--brand);
108
108
  --sidebar-primary-foreground: var(--brand-foreground);
109
- --sidebar-accent: oklch(0.97 0 0);
110
- --sidebar-accent-foreground: oklch(0.205 0 0);
111
- --sidebar-border: oklch(0.922 0 0);
109
+ --sidebar-accent: oklch(0.967 0.001 286.375);
110
+ --sidebar-accent-foreground: oklch(0.21 0.006 285.885);
111
+ --sidebar-border: oklch(0.92 0.004 286.32);
112
112
  --sidebar-ring: var(--brand);
113
113
  }
114
114
 
115
115
  .dark {
116
- --brand: oklch(0.585 0.233 293);
116
+ --brand: oklch(0.586 0.218 292.5);
117
117
  --brand-foreground: oklch(1 0 0);
118
- --brand-text: oklch(0.625 0.223 293);
119
- --brand-tint: oklch(0.3 0.04 293);
120
- --brand-tint-foreground: oklch(0.685 0.183 293);
121
- --brand-map: #8750f6;
118
+ --brand-text: oklch(0.626 0.218 292.5);
119
+ --brand-tint: oklch(0.3 0.04 292.5);
120
+ --brand-tint-foreground: oklch(0.686 0.181 292.5);
121
+ --brand-map: #8556ef;
122
122
 
123
- --background: oklch(0.145 0 0);
123
+ --background: oklch(0.141 0.005 285.823);
124
124
  --foreground: oklch(0.985 0 0);
125
- --card: oklch(0.205 0 0);
125
+ --card: oklch(0.21 0.006 285.885);
126
126
  --card-foreground: oklch(0.985 0 0);
127
- --popover: oklch(0.205 0 0);
127
+ --popover: oklch(0.21 0.006 285.885);
128
128
  --popover-foreground: oklch(0.985 0 0);
129
- --secondary: oklch(0.269 0 0);
129
+ --secondary: oklch(0.274 0.006 286.033);
130
130
  --secondary-foreground: oklch(0.985 0 0);
131
- --muted: oklch(0.269 0 0);
132
- --muted-foreground: oklch(0.708 0 0);
133
- --accent: oklch(0.269 0 0);
131
+ --muted: oklch(0.274 0.006 286.033);
132
+ --muted-foreground: oklch(0.705 0.015 286.067);
133
+ --accent: oklch(0.274 0.006 286.033);
134
134
  --accent-foreground: oklch(0.985 0 0);
135
- --destructive: oklch(0.704 0.191 22.216);
135
+ /* Lighter than nigam's red-400: destructive text on its 20% tint must reach 4.5:1 on dialog
136
+ footers (red-400 only reached 4.2). */
137
+ --destructive: oklch(0.76 0.191 22.216);
136
138
  --success: oklch(0.792 0.209 151.711);
137
139
  --warning: oklch(0.828 0.189 84.429);
138
140
  --info: oklch(0.707 0.165 254.624);
139
141
  --border: oklch(1 0 0 / 10%);
140
142
  --input: oklch(1 0 0 / 15%);
141
- --chart-2: oklch(0.556 0 0);
142
- --chart-3: oklch(0.439 0 0);
143
- --chart-4: oklch(0.371 0 0);
144
- --chart-5: oklch(0.269 0 0);
145
- --sidebar: oklch(0.205 0 0);
143
+ --chart-2: oklch(0.696 0.17 162.48);
144
+ --chart-3: oklch(0.769 0.188 70.08);
145
+ --chart-4: oklch(0.627 0.265 303.9);
146
+ --chart-5: oklch(0.645 0.246 16.439);
147
+ --sidebar: oklch(0.21 0.006 285.885);
146
148
  --sidebar-foreground: oklch(0.985 0 0);
147
- --sidebar-accent: oklch(0.269 0 0);
149
+ --sidebar-accent: oklch(0.274 0.006 286.033);
148
150
  --sidebar-accent-foreground: oklch(0.985 0 0);
149
151
  --sidebar-border: oklch(1 0 0 / 10%);
150
152
  }