@photon-ai/pho-ui 0.7.2 → 1.0.1

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 (121) hide show
  1. package/dist/chunks/avatar-CiAvJNNs.js +99 -0
  2. package/dist/chunks/avatar-CiAvJNNs.js.map +1 -0
  3. package/dist/chunks/close-button-8f-oRJXt.js +420 -0
  4. package/dist/chunks/close-button-8f-oRJXt.js.map +1 -0
  5. package/dist/chunks/{cn-7uGUzCOM.js → cn-DTiB6ek_.js} +4 -2
  6. package/dist/chunks/{cn-7uGUzCOM.js.map → cn-DTiB6ek_.js.map} +1 -1
  7. package/dist/chunks/dialog-Bgb2Z1_v.js +141 -0
  8. package/dist/chunks/dialog-Bgb2Z1_v.js.map +1 -0
  9. package/dist/chunks/{input-DbJpdu6v.js → input-BSJGBUbf.js} +36 -36
  10. package/dist/chunks/input-BSJGBUbf.js.map +1 -0
  11. package/dist/chunks/menu-Dan0oRvL.js +208 -0
  12. package/dist/chunks/menu-Dan0oRvL.js.map +1 -0
  13. package/dist/chunks/{sidebar-CivriTLb.js → sidebar-Buhva5xa.js} +13 -13
  14. package/dist/chunks/sidebar-Buhva5xa.js.map +1 -0
  15. package/dist/chunks/spinner-Cy8bkyJf.js +54 -0
  16. package/dist/chunks/spinner-Cy8bkyJf.js.map +1 -0
  17. package/dist/components/accordion.js +21 -21
  18. package/dist/components/accordion.js.map +1 -1
  19. package/dist/components/alert-dialog.js +26 -26
  20. package/dist/components/alert-dialog.js.map +1 -1
  21. package/dist/components/autocomplete.js +46 -46
  22. package/dist/components/autocomplete.js.map +1 -1
  23. package/dist/components/avatar.js +1 -1
  24. package/dist/components/badge.js +1 -1
  25. package/dist/components/button.js +11 -11
  26. package/dist/components/checkbox.js +3 -3
  27. package/dist/components/checkbox.js.map +1 -1
  28. package/dist/components/collapsible.js +8 -8
  29. package/dist/components/collapsible.js.map +1 -1
  30. package/dist/components/combobox.js +69 -69
  31. package/dist/components/combobox.js.map +1 -1
  32. package/dist/components/context-menu.js +55 -39
  33. package/dist/components/context-menu.js.map +1 -1
  34. package/dist/components/dialog.js +3 -2
  35. package/dist/components/drawer.js +15 -15
  36. package/dist/components/drawer.js.map +1 -1
  37. package/dist/components/field.js +18 -18
  38. package/dist/components/field.js.map +1 -1
  39. package/dist/components/fieldset.js +9 -9
  40. package/dist/components/fieldset.js.map +1 -1
  41. package/dist/components/form.js +1 -1
  42. package/dist/components/input.js +1 -1
  43. package/dist/components/kbd.js +6 -6
  44. package/dist/components/kbd.js.map +1 -1
  45. package/dist/components/menu.js +6 -4
  46. package/dist/components/menubar.js +5 -5
  47. package/dist/components/menubar.js.map +1 -1
  48. package/dist/components/meter.js +20 -20
  49. package/dist/components/meter.js.map +1 -1
  50. package/dist/components/navigation-menu.js +8 -8
  51. package/dist/components/navigation-menu.js.map +1 -1
  52. package/dist/components/notification.js +24 -24
  53. package/dist/components/notification.js.map +1 -1
  54. package/dist/components/number-field.js +32 -32
  55. package/dist/components/number-field.js.map +1 -1
  56. package/dist/components/otp-field.js +5 -5
  57. package/dist/components/otp-field.js.map +1 -1
  58. package/dist/components/popover.js +10 -10
  59. package/dist/components/popover.js.map +1 -1
  60. package/dist/components/preview-card.js +8 -8
  61. package/dist/components/preview-card.js.map +1 -1
  62. package/dist/components/progress.js +25 -25
  63. package/dist/components/progress.js.map +1 -1
  64. package/dist/components/radio-group.js +13 -13
  65. package/dist/components/radio-group.js.map +1 -1
  66. package/dist/components/scroll-area.js +1 -1
  67. package/dist/components/select.js +35 -35
  68. package/dist/components/select.js.map +1 -1
  69. package/dist/components/separator.js +1 -1
  70. package/dist/components/slider.js +17 -17
  71. package/dist/components/slider.js.map +1 -1
  72. package/dist/components/spinner.js +6 -0
  73. package/dist/components/switch.js +10 -10
  74. package/dist/components/switch.js.map +1 -1
  75. package/dist/components/tabs.js +16 -16
  76. package/dist/components/tabs.js.map +1 -1
  77. package/dist/components/theme-toggle.js +203 -0
  78. package/dist/components/theme-toggle.js.map +1 -0
  79. package/dist/components/toast.js +24 -24
  80. package/dist/components/toast.js.map +1 -1
  81. package/dist/components/toggle-group.js +1 -1
  82. package/dist/components/toggle.js +5 -5
  83. package/dist/components/toggle.js.map +1 -1
  84. package/dist/components/toolbar.js +8 -8
  85. package/dist/components/toolbar.js.map +1 -1
  86. package/dist/components/tooltip.js +10 -10
  87. package/dist/components/tooltip.js.map +1 -1
  88. package/dist/index.js +29 -28
  89. package/dist/sections/sidebar.js +1 -1
  90. package/dist/src/components/avatar/avatar.d.ts +24 -2
  91. package/dist/src/components/avatar/index.d.ts +1 -1
  92. package/dist/src/components/button/button.d.ts +58 -44
  93. package/dist/src/components/button/index.d.ts +1 -1
  94. package/dist/src/components/dialog/dialog.d.ts +50 -7
  95. package/dist/src/components/dialog/index.d.ts +1 -1
  96. package/dist/src/components/menu/index.d.ts +1 -1
  97. package/dist/src/components/menu/menu-pending.d.ts +43 -0
  98. package/dist/src/components/spinner/index.d.ts +1 -0
  99. package/dist/src/components/spinner/spinner.d.ts +32 -0
  100. package/dist/src/components/theme-toggle/index.d.ts +1 -0
  101. package/dist/src/components/theme-toggle/theme-toggle.d.ts +81 -0
  102. package/dist/src/sections/sidebar/sidebar.d.ts +5 -6
  103. package/dist/utils.js +1 -1
  104. package/package.json +9 -1
  105. package/src/styles/fonts.css +14 -18
  106. package/src/styles/pho.css +5 -1
  107. package/src/styles/theme.css +183 -147
  108. package/dist/chunks/avatar-Bb4RZQIa.js +0 -53
  109. package/dist/chunks/avatar-Bb4RZQIa.js.map +0 -1
  110. package/dist/chunks/button-CQ4cZvkC.js +0 -365
  111. package/dist/chunks/button-CQ4cZvkC.js.map +0 -1
  112. package/dist/chunks/close-button-CNhn8z0w.js +0 -29
  113. package/dist/chunks/close-button-CNhn8z0w.js.map +0 -1
  114. package/dist/chunks/dialog-7oOe-eaO.js +0 -63
  115. package/dist/chunks/dialog-7oOe-eaO.js.map +0 -1
  116. package/dist/chunks/input-DbJpdu6v.js.map +0 -1
  117. package/dist/chunks/menu-BfaNHnOp.js +0 -101
  118. package/dist/chunks/menu-BfaNHnOp.js.map +0 -1
  119. package/dist/chunks/sidebar-CivriTLb.js.map +0 -1
  120. package/dist/src/components/context-menu/context-menu.d.ts +0 -34
  121. package/dist/src/components/menu/menu.d.ts +0 -47
@@ -0,0 +1,99 @@
1
+ "use client";
2
+ import { t as n } from "./cn-DTiB6ek_.js";
3
+ import { t as x } from "./resolve-variant-CcD8iG5l.js";
4
+ import { n as z } from "./spinner-Cy8bkyJf.js";
5
+ import { t as k } from "./createReactComponent-BqCmnqfX.js";
6
+ import { forwardRef as N, useEffect as E, useRef as j, useState as R } from "react";
7
+ import { jsx as s } from "react/jsx-runtime";
8
+ import { Avatar as c } from "@base-ui/react/avatar";
9
+ var A = [["path", {
10
+ d: "M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4",
11
+ key: "svg-0"
12
+ }], ["path", {
13
+ d: "M13.5 6.5l4 4",
14
+ key: "svg-1"
15
+ }]], S = k("outline", "pencil", "Pencil", A), _ = {
16
+ xs: {
17
+ classes: "size-6 text-[0.625rem]",
18
+ description: "Extra small — inline chips and rails"
19
+ },
20
+ sm: {
21
+ classes: "size-8 text-xs",
22
+ description: "Small — dense lists"
23
+ },
24
+ md: {
25
+ classes: "size-10 text-base",
26
+ description: "Medium — the default"
27
+ },
28
+ lg: {
29
+ classes: "size-12 text-md",
30
+ description: "Large — profile headers"
31
+ }
32
+ };
33
+ function w({ className: e, size: t = "md", ...a }) {
34
+ return /* @__PURE__ */ s(c.Root, {
35
+ className: n("group bg-pho-secondary text-pho-secondary relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full align-middle font-medium select-none", x(_, t), e),
36
+ ...a
37
+ });
38
+ }
39
+ function C({ className: e, ...t }) {
40
+ return /* @__PURE__ */ s(c.Image, {
41
+ className: n("size-full object-cover", e),
42
+ ...t
43
+ });
44
+ }
45
+ function I({ className: e, ...t }) {
46
+ return /* @__PURE__ */ s(c.Fallback, {
47
+ className: n("flex size-full items-center justify-center", e),
48
+ ...t
49
+ });
50
+ }
51
+ function M(e) {
52
+ return e != null && typeof e.then == "function";
53
+ }
54
+ var V = N(function({ label: t, className: a, children: u, onClick: d, loading: p = !1, disabled: o = !1, ...m }, v) {
55
+ const [y, l] = R(!1), r = j(!0);
56
+ E(() => (r.current = !0, () => {
57
+ r.current = !1;
58
+ }), []);
59
+ const i = y || p;
60
+ function h(b) {
61
+ if (i || o) return;
62
+ const f = d?.(b);
63
+ M(f) && (l(!0), f.then(() => {
64
+ r.current && l(!1);
65
+ }, (g) => {
66
+ r.current && l(!1), typeof process < "u" && process.env.NODE_ENV !== "production" && console.error("Avatar.Edit: async onClick rejected —", g);
67
+ }));
68
+ }
69
+ return /* @__PURE__ */ s("button", {
70
+ ref: v,
71
+ type: "button",
72
+ "aria-label": t || "Edit",
73
+ "aria-busy": i || void 0,
74
+ "aria-disabled": o || i || void 0,
75
+ disabled: o,
76
+ "data-loading": i || void 0,
77
+ ...m,
78
+ onClick: h,
79
+ className: n("bg-pho-overlay outline-pho-brand absolute inset-0 z-10 flex cursor-pointer items-center justify-center text-white opacity-0 transition-opacity duration-100 ease-linear group-hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-2 data-loading:cursor-wait data-loading:opacity-100 data-[popup-open]:opacity-100", a),
80
+ children: i ? /* @__PURE__ */ s(z, { className: "size-[45%]" }) : u != null ? /* @__PURE__ */ s("span", {
81
+ className: "flex size-[45%] shrink-0 items-center justify-center [&_svg]:size-full",
82
+ children: u
83
+ }) : /* @__PURE__ */ s(S, {
84
+ "aria-hidden": "true",
85
+ className: "size-[45%] shrink-0"
86
+ })
87
+ });
88
+ }), Z = {
89
+ Root: w,
90
+ Image: C,
91
+ Fallback: I,
92
+ Edit: V
93
+ };
94
+ export {
95
+ Z as n,
96
+ _ as t
97
+ };
98
+
99
+ //# sourceMappingURL=avatar-CiAvJNNs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"avatar-CiAvJNNs.js","names":[],"sources":["../../../../node_modules/.pnpm/@tabler+icons-react@3.44.0_react@19.2.7/node_modules/@tabler/icons-react/dist/esm/icons/IconPencil.mjs","../../src/components/avatar/avatar.tsx"],"sourcesContent":["/**\n * @license @tabler/icons-react v3.44.0 - MIT\n *\n * This source code is licensed under the MIT license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createReactComponent from '../createReactComponent.mjs';\n\nconst __iconNode = [[\"path\", { \"d\": \"M4 20h4l10.5 -10.5a2.828 2.828 0 1 0 -4 -4l-10.5 10.5v4\", \"key\": \"svg-0\" }], [\"path\", { \"d\": \"M13.5 6.5l4 4\", \"key\": \"svg-1\" }]];\nconst IconPencil = createReactComponent(\"outline\", \"pencil\", \"Pencil\", __iconNode);\n\nexport { __iconNode, IconPencil as default };\n//# sourceMappingURL=IconPencil.mjs.map\n","import {\n forwardRef,\n useEffect,\n useRef,\n useState,\n type ButtonHTMLAttributes,\n type ComponentProps,\n type ElementType,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n} from \"react\";\nimport { Avatar as BaseAvatar } from \"@base-ui/react/avatar\";\nimport { IconPencil } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils/cn\";\nimport { resolveVariant, type VariantMap } from \"../../utils/resolve-variant\";\nimport { Spinner } from \"../spinner/spinner\";\n\n/** Override Base UI's `string | (state) => string` className with plain string. */\ntype StyledProps<T extends ElementType> = Omit<\n ComponentProps<T>,\n \"className\"\n> & {\n className?: string;\n};\n\n/** Size axis — four sizes, no `xl` (Geist-reductive). `md` is default. */\nexport const AVATAR_SIZES = {\n xs: {\n classes: \"size-6 text-[0.625rem]\",\n description: \"Extra small — inline chips and rails\",\n },\n sm: { classes: \"size-8 text-xs\", description: \"Small — dense lists\" },\n md: { classes: \"size-10 text-base\", description: \"Medium — the default\" },\n lg: { classes: \"size-12 text-md\", description: \"Large — profile headers\" },\n} satisfies VariantMap;\n\nexport type AvatarSize = keyof typeof AVATAR_SIZES;\n\ninterface RootProps extends StyledProps<typeof BaseAvatar.Root> {\n /** Size variant. @default \"md\" */\n size?: AvatarSize;\n}\n\nfunction Root({ className, size = \"md\", ...props }: RootProps) {\n return (\n <BaseAvatar.Root\n className={cn(\n // True circle at rest — `squircle` is for interactive chrome (hover /\n // rings), not resting media. `group` drives Avatar.Edit hover reveal.\n \"group bg-pho-secondary text-pho-secondary relative inline-flex shrink-0 items-center justify-center overflow-hidden rounded-full align-middle font-medium select-none\",\n resolveVariant(AVATAR_SIZES, size),\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction Image({ className, ...props }: StyledProps<typeof BaseAvatar.Image>) {\n return (\n <BaseAvatar.Image\n className={cn(\"size-full object-cover\", className)}\n {...props}\n />\n );\n}\n\nfunction Fallback({\n className,\n ...props\n}: StyledProps<typeof BaseAvatar.Fallback>) {\n return (\n <BaseAvatar.Fallback\n className={cn(\"flex size-full items-center justify-center\", className)}\n {...props}\n />\n );\n}\n\nfunction isThenable(value: unknown): value is Promise<unknown> {\n return (\n value != null && typeof (value as { then?: unknown }).then === \"function\"\n );\n}\n\nexport interface EditProps extends Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n \"color\" | \"onClick\"\n> {\n /** Accessible label. @default \"Edit\" */\n label?: string;\n /**\n * Replace the default pencil icon. Pass `IconDots` when this button is a\n * `Menu.Trigger` (change + remove). Custom icons are sized automatically.\n */\n children?: ReactNode;\n /**\n * Declarative loading state (same visual as an in-flight promise).\n * @default false\n */\n loading?: boolean;\n /**\n * Action handler. Return a promise to keep the overlay visible with a\n * spinner until it settles. On rejection the control stays interactive —\n * surface errors inside the handler.\n */\n onClick?: (\n event: ReactMouseEvent<HTMLButtonElement>,\n ) => void | Promise<unknown>;\n}\n\n/**\n * Full-face edit control — overlay + icon that reveals on hover /\n * focus-visible / open menu. Default icon is pencil for a direct action;\n * pass children (e.g. `IconDots`) when composing a Menu. Wire behavior via\n * `onClick` (sync or async) or `Menu.Trigger` — no file input is built in.\n */\nconst Edit = forwardRef<HTMLButtonElement, EditProps>(function Edit(\n {\n label,\n className,\n children,\n onClick,\n loading = false,\n disabled = false,\n ...props\n },\n ref,\n) {\n const [busy, setBusy] = useState(false);\n const mountedRef = useRef(true);\n\n useEffect(() => {\n mountedRef.current = true;\n return () => {\n mountedRef.current = false;\n };\n }, []);\n\n const inert = busy || loading;\n\n function handleClick(event: ReactMouseEvent<HTMLButtonElement>) {\n if (inert || disabled) return;\n const result = onClick?.(event);\n if (!isThenable(result)) return;\n setBusy(true);\n result.then(\n () => {\n if (mountedRef.current) setBusy(false);\n },\n (error: unknown) => {\n if (mountedRef.current) setBusy(false);\n if (\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\"\n ) {\n console.error(\"Avatar.Edit: async onClick rejected —\", error);\n }\n },\n );\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n aria-label={label || \"Edit\"}\n aria-busy={inert || undefined}\n aria-disabled={disabled || inert || undefined}\n disabled={disabled}\n data-loading={inert || undefined}\n {...props}\n onClick={handleClick}\n className={cn(\n // Stay visible while a Menu opened from this trigger is open\n // (`data-popup-open` from Base UI), or while an async action runs.\n \"bg-pho-overlay outline-pho-brand absolute inset-0 z-10 flex cursor-pointer items-center justify-center text-white opacity-0 transition-opacity duration-100 ease-linear group-hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-2 focus-visible:outline-offset-2 data-loading:cursor-wait data-loading:opacity-100 data-[popup-open]:opacity-100\",\n className,\n )}\n >\n {inert ? (\n <Spinner className=\"size-[45%]\" />\n ) : children != null ? (\n <span className=\"flex size-[45%] shrink-0 items-center justify-center [&_svg]:size-full\">\n {children}\n </span>\n ) : (\n <IconPencil aria-hidden=\"true\" className=\"size-[45%] shrink-0\" />\n )}\n </button>\n );\n});\n\n/**\n * Avatar built on Base UI `Avatar`. Compose `Avatar.Image` with an\n * `Avatar.Fallback` (initials or icon) that shows while the image loads or if\n * it fails. Set `size` on `Avatar.Root`. Add `Avatar.Edit` when the avatar\n * itself should open a change-photo action.\n */\nexport const Avatar = { Root, Image, Fallback, Edit };\n"],"x_google_ignoreList":[0],"mappings":"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"}
@@ -0,0 +1,420 @@
1
+ "use client";
2
+ import { t as p } from "./cn-DTiB6ek_.js";
3
+ import { n as G, r as C } from "./link-provider-9TFNyDqO.js";
4
+ import { t as N } from "./resolve-variant-CcD8iG5l.js";
5
+ import { n as T } from "./spinner-Cy8bkyJf.js";
6
+ import { t as Z } from "./IconX-pZ9vrtSr.js";
7
+ import { isValidElement as B, useMemo as K } from "react";
8
+ import { Fragment as Q, jsx as t, jsxs as W } from "react/jsx-runtime";
9
+ import { motion as w } from "motion/react";
10
+ var X = p("group relative inline-flex h-max cursor-pointer items-center justify-center whitespace-nowrap outline-pho-brand transition duration-100 ease-linear focus-visible:outline-2 focus-visible:outline-offset-2", "disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:pointer-events-none"), L = {
11
+ sm: {
12
+ classes: p("gap-1 px-3.5 py-1.5 text-base font-medium data-svg-only:p-2", "has-[[data-icon=prefix]]:pl-2 has-[[data-icon=loading]]:pl-2 has-[[data-icon=suffix]]:pr-2"),
13
+ description: "Small — compact rows, tables, and toolbars"
14
+ },
15
+ md: {
16
+ classes: p("gap-1 px-4 py-2 text-base font-medium data-svg-only:p-2.5", "has-[[data-icon=prefix]]:pl-2.5 has-[[data-icon=loading]]:pl-2.5 has-[[data-icon=suffix]]:pr-2.5"),
17
+ description: "Medium — the default"
18
+ },
19
+ lg: {
20
+ classes: p("gap-1.5 px-5 py-2.5 text-md font-medium data-svg-only:p-3", "has-[[data-icon=prefix]]:pl-3 has-[[data-icon=loading]]:pl-3 has-[[data-icon=suffix]]:pr-3"),
21
+ description: "Large — prominent and marketing actions"
22
+ }
23
+ }, z = {
24
+ rounded: {
25
+ classes: "rounded-full",
26
+ description: "Pill — the default; every button on photon.codes"
27
+ },
28
+ square: {
29
+ classes: "rounded-base",
30
+ description: "Gently rounded corners for dense app chrome"
31
+ },
32
+ circle: {
33
+ classes: "rounded-full",
34
+ description: "Circular — pair with svgOnly for round icon buttons"
35
+ }
36
+ }, J = "squircle", Y = {
37
+ sm: "py-[4.5px] data-svg-only:p-[6.5px]",
38
+ md: "py-[6.5px] data-svg-only:p-[8.5px]",
39
+ lg: "py-[8.5px] data-svg-only:p-[10.5px]"
40
+ }, ge = {
41
+ effect: {
42
+ classes: "",
43
+ description: "Default — marketing pill with same-hue outer rim and inset sheen"
44
+ },
45
+ filled: {
46
+ classes: "",
47
+ description: "Solid fill without sheen"
48
+ },
49
+ outlined: {
50
+ classes: "",
51
+ description: "Hairline ring — supporting action"
52
+ },
53
+ ghost: {
54
+ classes: "",
55
+ description: "Borderless — quiet / inline action"
56
+ }
57
+ }, xe = {
58
+ default: {
59
+ classes: "",
60
+ description: "Neutral / brand contrast"
61
+ },
62
+ destructive: {
63
+ classes: "",
64
+ description: "Destructive intent (delete, discard)"
65
+ }
66
+ }, $ = {
67
+ effect: {
68
+ default: p("squircle overflow-hidden bg-pho-brand-solid text-pho-on-brand transition-none", "hover:bg-pho-brand-solid data-loading:bg-pho-brand-solid", "[--pho-rim:color-mix(in_oklab,var(--background-color-pho-brand-solid)_62%,transparent)]", "[--pho-sheen:light-dark(rgba(255,255,255,0.5),rgba(0,0,0,0.12))]", "shadow-[0_0_0_1.5px_var(--pho-rim),inset_0_0_0_1px_var(--border-color-pho-on-brand),inset_0_var(--pho-sheen-y,0px)_var(--pho-sheen-blur,5px)_var(--pho-sheen-spread,2px)_var(--pho-sheen)]"),
69
+ destructive: p("squircle overflow-hidden bg-pho-error-solid text-white outline-pho-error transition-none", "hover:bg-pho-error-solid data-loading:bg-pho-error-solid", "[--pho-rim:color-mix(in_oklab,var(--background-color-pho-error-solid)_62%,transparent)]", "[--pho-sheen:light-dark(rgba(255,255,255,0.45),rgba(0,0,0,0.18))]", "shadow-[0_0_0_1.5px_var(--pho-rim),inset_0_0_0_1px_oklch(1_0_0/0.2),inset_0_var(--pho-sheen-y,0px)_var(--pho-sheen-blur,5px)_var(--pho-sheen-spread,2px)_var(--pho-sheen)]")
70
+ },
71
+ filled: {
72
+ default: "bg-pho-brand-solid text-pho-on-brand hover:bg-pho-brand-solid-hover data-loading:bg-pho-brand-solid-hover",
73
+ destructive: "bg-pho-error-solid text-white outline-pho-error hover:bg-pho-error-solid-hover data-loading:bg-pho-error-solid-hover"
74
+ },
75
+ outlined: {
76
+ default: "bg-pho-primary text-pho-secondary ring-1 ring-pho-secondary ring-inset hover:bg-pho-primary-hover hover:text-pho-secondary-hover data-loading:bg-pho-primary-hover",
77
+ destructive: "bg-pho-primary text-pho-error outline-pho-error ring-1 ring-pho-error ring-inset hover:bg-pho-error-primary hover:text-pho-error-hover data-loading:bg-pho-error-primary"
78
+ },
79
+ ghost: {
80
+ default: "text-pho-secondary hover:bg-pho-ghost-hover hover:text-pho-secondary-hover data-loading:bg-pho-ghost-hover",
81
+ destructive: "text-pho-error outline-pho-error hover:bg-pho-error-primary hover:text-pho-error-hover data-loading:bg-pho-error-primary"
82
+ }
83
+ };
84
+ function O(e) {
85
+ const { variant: r = "effect", color: o = "default", size: a = "md", shape: n = "rounded", className: i } = e ?? {}, d = r === "effect" ? "rounded" : n, s = $[r]?.[o] ?? "";
86
+ return p(X, N(z, d), d === "square" && a === "lg" && J, N(L, a), r === "effect" && Y[a], s, i);
87
+ }
88
+ var ee = "transition-inherit-all pointer-events-none inline-flex shrink-0 items-center [&>svg]:size-[1.125em] [&>svg]:shrink-0";
89
+ function S({ position: e, children: r }) {
90
+ return /* @__PURE__ */ t("span", {
91
+ "data-icon": e,
92
+ className: ee,
93
+ children: r
94
+ });
95
+ }
96
+ function R(e, r) {
97
+ return e == null || e === !1 ? null : C(e) ? /* @__PURE__ */ t(S, {
98
+ position: r,
99
+ children: /* @__PURE__ */ t(e, {})
100
+ }) : B(e) ? /* @__PURE__ */ t(S, {
101
+ position: r,
102
+ children: e
103
+ }) : null;
104
+ }
105
+ function I(e, r) {
106
+ e && !r && typeof process < "u" && process.env.NODE_ENV !== "production" && console.warn("Button: `svgOnly` buttons require an `aria-label` that names the action.");
107
+ }
108
+ var A = {
109
+ type: "spring",
110
+ stiffness: 200,
111
+ damping: 30,
112
+ mass: 0.1
113
+ }, re = {
114
+ "--pho-sheen-y": "0px",
115
+ "--pho-sheen-blur": "5px",
116
+ "--pho-sheen-spread": "2px"
117
+ }, oe = {
118
+ sm: {
119
+ "--pho-sheen-y": "-3px",
120
+ "--pho-sheen-blur": "8px",
121
+ "--pho-sheen-spread": "2px"
122
+ },
123
+ md: {
124
+ "--pho-sheen-y": "-3px",
125
+ "--pho-sheen-blur": "8px",
126
+ "--pho-sheen-spread": "2px"
127
+ },
128
+ lg: {
129
+ "--pho-sheen-y": "-6px",
130
+ "--pho-sheen-blur": "14px",
131
+ "--pho-sheen-spread": "1px"
132
+ }
133
+ }, ae = {
134
+ sm: {
135
+ "--pho-sheen-y": "1.5px",
136
+ "--pho-sheen-blur": "4px",
137
+ "--pho-sheen-spread": "1px"
138
+ },
139
+ md: {
140
+ "--pho-sheen-y": "1.5px",
141
+ "--pho-sheen-blur": "4px",
142
+ "--pho-sheen-spread": "1px"
143
+ },
144
+ lg: {
145
+ "--pho-sheen-y": "3px",
146
+ "--pho-sheen-blur": "7px",
147
+ "--pho-sheen-spread": "1px"
148
+ }
149
+ }, te = 0.97, ne = {
150
+ sm: {
151
+ rest: {
152
+ opacity: 0,
153
+ width: 1,
154
+ height: 9
155
+ },
156
+ hover: {
157
+ opacity: 1,
158
+ width: 17,
159
+ height: 18
160
+ }
161
+ },
162
+ md: {
163
+ rest: {
164
+ opacity: 0,
165
+ width: 1,
166
+ height: 9
167
+ },
168
+ hover: {
169
+ opacity: 1,
170
+ width: 17,
171
+ height: 18
172
+ }
173
+ },
174
+ lg: {
175
+ rest: {
176
+ opacity: 0,
177
+ width: 1,
178
+ height: 10
179
+ },
180
+ hover: {
181
+ opacity: 1,
182
+ width: 20,
183
+ height: 21
184
+ }
185
+ }
186
+ }, F = {
187
+ sm: "has-[[data-icon=prefix]]:pl-3.5 has-[[data-icon=loading]]:pl-3.5 has-[[data-icon=suffix]]:pr-3.5",
188
+ md: "has-[[data-icon=prefix]]:pl-4 has-[[data-icon=loading]]:pl-4 has-[[data-icon=suffix]]:pr-4",
189
+ lg: "has-[[data-icon=prefix]]:pl-5 has-[[data-icon=loading]]:pl-5 has-[[data-icon=suffix]]:pr-5"
190
+ };
191
+ function E({ position: e, size: r, children: o }) {
192
+ return /* @__PURE__ */ t(w.span, {
193
+ "data-icon": e,
194
+ variants: ne[r],
195
+ transition: A,
196
+ style: {
197
+ display: "inline-flex",
198
+ flex: "none",
199
+ alignItems: "center",
200
+ overflow: "hidden"
201
+ },
202
+ className: "pointer-events-none [&>svg]:block [&>svg]:size-full",
203
+ children: o
204
+ });
205
+ }
206
+ function k(e, r, o) {
207
+ if (!(e == null || e === !1))
208
+ return C(e) ? /* @__PURE__ */ t(E, {
209
+ position: r,
210
+ size: o,
211
+ children: /* @__PURE__ */ t(e, {})
212
+ }) : B(e) ? /* @__PURE__ */ t(E, {
213
+ position: r,
214
+ size: o,
215
+ children: e
216
+ }) : /* @__PURE__ */ t(E, {
217
+ position: r,
218
+ size: o,
219
+ children: e
220
+ });
221
+ }
222
+ function V({ svgOnly: e, loading: r, prefix: o, suffix: a, children: n, iconReveal: i, size: d }) {
223
+ if (e) return r ? /* @__PURE__ */ t(T, {}) : n;
224
+ const s = !!i;
225
+ return /* @__PURE__ */ W(Q, { children: [
226
+ r ? /* @__PURE__ */ t(S, {
227
+ position: "loading",
228
+ children: /* @__PURE__ */ t(T, {})
229
+ }) : s ? k(o, "prefix", d) : R(o, "prefix"),
230
+ n != null && n !== !1 && /* @__PURE__ */ t("span", {
231
+ "data-text": !0,
232
+ className: "transition-inherit-all px-0.5",
233
+ children: n
234
+ }),
235
+ s ? k(a, "suffix", d) : R(a, "suffix")
236
+ ] });
237
+ }
238
+ function P(e, r, o) {
239
+ const a = o.sheen ? {
240
+ ...re,
241
+ scale: 1
242
+ } : {}, n = o.sheen ? { ...oe[e] } : {}, i = o.sheen ? {
243
+ ...ae[e],
244
+ scale: te
245
+ } : {};
246
+ return o.iconReveal && (a.gap = 0, n.gap = e === "lg" ? 4 : 0), {
247
+ initial: !1,
248
+ animate: "rest",
249
+ whileHover: r ? void 0 : "hover",
250
+ whileTap: r || !o.sheen ? void 0 : "press",
251
+ variants: {
252
+ rest: a,
253
+ hover: n,
254
+ press: i
255
+ },
256
+ transition: A
257
+ };
258
+ }
259
+ function U({ variant: e, color: r, size: o, shape: a, loading: n, svgOnly: i, className: d }) {
260
+ return p(O({
261
+ variant: e,
262
+ color: r,
263
+ size: o,
264
+ shape: a
265
+ }), n && "pointer-events-none", i && "[&>svg]:size-[1.25em] [&>svg]:shrink-0", d);
266
+ }
267
+ function q({ variant: e = "effect", color: r, size: o = "md", shape: a, prefix: n, suffix: i, iconReveal: d = !1, svgOnly: s, loading: l, children: b, className: x, ref: g, ...f }) {
268
+ I(s, f["aria-label"]);
269
+ const c = e === "effect", h = d && !s, v = U({
270
+ variant: e,
271
+ color: r,
272
+ size: o,
273
+ shape: a,
274
+ loading: l,
275
+ svgOnly: s,
276
+ className: p(h && F[o], x)
277
+ }), u = V({
278
+ svgOnly: s,
279
+ loading: l,
280
+ prefix: n,
281
+ suffix: i,
282
+ children: b,
283
+ iconReveal: h,
284
+ size: o
285
+ });
286
+ return c || h ? /* @__PURE__ */ t(w.button, {
287
+ ref: g,
288
+ type: "button",
289
+ ...P(o, f.disabled || l, {
290
+ sheen: c,
291
+ iconReveal: h
292
+ }),
293
+ ...f,
294
+ "aria-busy": l || void 0,
295
+ "data-loading": l ? "true" : void 0,
296
+ "data-svg-only": s ? "true" : void 0,
297
+ className: v,
298
+ children: u
299
+ }) : /* @__PURE__ */ t("button", {
300
+ ref: g,
301
+ type: "button",
302
+ ...f,
303
+ "aria-busy": l || void 0,
304
+ "data-loading": l ? "true" : void 0,
305
+ "data-svg-only": s ? "true" : void 0,
306
+ className: v,
307
+ children: u
308
+ });
309
+ }
310
+ q.displayName = "Button";
311
+ function D({ variant: e = "effect", color: r, size: o = "md", shape: a, prefix: n, suffix: i, iconReveal: d = !1, svgOnly: s, loading: l, disabled: b, href: x, children: g, className: f, ...c }) {
312
+ I(s, c["aria-label"]);
313
+ const h = G(), v = e === "effect", u = d && !s, H = U({
314
+ variant: e,
315
+ color: r,
316
+ size: o,
317
+ shape: a,
318
+ loading: l,
319
+ svgOnly: s,
320
+ className: p(u && F[o], f)
321
+ }), _ = V({
322
+ svgOnly: s,
323
+ loading: l,
324
+ prefix: n,
325
+ suffix: i,
326
+ children: g,
327
+ iconReveal: u,
328
+ size: o
329
+ }), M = K(() => w.create(h), [h]), m = !!(b || l), y = {
330
+ ...c,
331
+ ...m ? {} : { href: x },
332
+ "aria-disabled": m || void 0,
333
+ "aria-busy": l || void 0,
334
+ "data-loading": l ? "true" : void 0,
335
+ "data-svg-only": s ? "true" : void 0,
336
+ className: H,
337
+ tabIndex: m ? -1 : c.tabIndex,
338
+ onClick: m ? (j) => {
339
+ j.preventDefault();
340
+ } : c.onClick
341
+ };
342
+ return m ? /* @__PURE__ */ t("span", {
343
+ ...y,
344
+ children: _
345
+ }) : v || u ? /* @__PURE__ */ t(M, {
346
+ ...P(o, b || l, {
347
+ sheen: v,
348
+ iconReveal: u
349
+ }),
350
+ ...y,
351
+ children: _
352
+ }) : /* @__PURE__ */ t(h, {
353
+ ...y,
354
+ children: _
355
+ });
356
+ }
357
+ D.displayName = "ButtonLink";
358
+ var _e = L;
359
+ function ye(e) {
360
+ const { size: r = "md", fullWidth: o, className: a } = e ?? {};
361
+ return O({
362
+ variant: "effect",
363
+ size: r,
364
+ className: p(o && "w-full", a)
365
+ });
366
+ }
367
+ function se({ size: e = "md", fullWidth: r, animated: o, iconReveal: a, className: n, ...i }) {
368
+ return /* @__PURE__ */ t(q, {
369
+ variant: "effect",
370
+ size: e,
371
+ iconReveal: a ?? o ?? !0,
372
+ className: p(r && "w-full", n),
373
+ ...i
374
+ });
375
+ }
376
+ se.displayName = "EffectButton";
377
+ function ie({ size: e = "md", fullWidth: r, animated: o, iconReveal: a, className: n, ...i }) {
378
+ return /* @__PURE__ */ t(D, {
379
+ variant: "effect",
380
+ size: e,
381
+ iconReveal: a ?? o ?? !0,
382
+ className: p(r && "w-full", n),
383
+ ...i
384
+ });
385
+ }
386
+ ie.displayName = "EffectButtonLink";
387
+ var le = {
388
+ sm: { classes: "size-9 rounded-base *:size-5" },
389
+ md: { classes: "size-10 rounded-base *:size-5" },
390
+ lg: { classes: "size-11 rounded-base *:size-6" }
391
+ };
392
+ function pe({ size: e = "sm", label: r, className: o, ...a }) {
393
+ return /* @__PURE__ */ t("button", {
394
+ type: "button",
395
+ "aria-label": r || "Close",
396
+ ...a,
397
+ className: p("squircle text-pho-secondary outline-pho-brand hover:bg-pho-ghost-hover hover:text-pho-secondary-hover inline-flex cursor-pointer items-center justify-center p-2 transition duration-100 ease-linear focus-visible:outline-2 focus-visible:outline-offset-2", N(le, e), o),
398
+ children: /* @__PURE__ */ t(Z, {
399
+ "aria-hidden": "true",
400
+ className: "transition-inherit-all shrink-0"
401
+ })
402
+ });
403
+ }
404
+ pe.displayName = "CloseButton";
405
+ export {
406
+ ge as a,
407
+ _e as c,
408
+ O as d,
409
+ ye as f,
410
+ L as i,
411
+ se as l,
412
+ xe as n,
413
+ q as o,
414
+ z as r,
415
+ D as s,
416
+ pe as t,
417
+ ie as u
418
+ };
419
+
420
+ //# sourceMappingURL=close-button-8f-oRJXt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"close-button-8f-oRJXt.js","names":[],"sources":["../../src/components/button/button.tsx","../../src/components/button/close-button.tsx"],"sourcesContent":["import {\n isValidElement,\n useMemo,\n type AnchorHTMLAttributes,\n type ButtonHTMLAttributes,\n type ComponentType,\n type FC,\n type MouseEvent as ReactMouseEvent,\n type ReactNode,\n type Ref,\n} from \"react\";\nimport { motion } from \"motion/react\";\nimport { cn } from \"../../utils/cn\";\nimport { resolveVariant, type VariantMap } from \"../../utils/resolve-variant\";\nimport { isReactComponent } from \"../../utils/is-react-component\";\nimport { useLinkComponent } from \"../../utils/link-provider\";\nimport { Spinner } from \"../spinner/spinner\";\n\n/**\n * Design notes:\n * - TWO visual axes: `variant` is the surface (filled / outlined / ghost /\n * effect) and `color` is the semantic ink (default / destructive). Dialog\n * confirms use the default `effect`; danger-zone triggers are `ghost` +\n * `destructive`. `effect` is the marketing pill: same-hue 1.5px outer rim\n * plus inset sheen that springs on hover.\n * - `iconReveal` is a separate boolean: on hover, prefix/suffix icons spring\n * in from collapsed — works with any variant, independent of `effect`.\n * - THREE sizes (`sm` / `md` / `lg`, default `md`). No `xs` / `xl`.\n * - `prefix` / `suffix` are generic slots (icon, spinner, count…), not\n * icon-only props. `svgOnly` makes an icon button and *requires* an\n * `aria-label`. `loading` is declarative. Effect’s rim/sheen are\n * box-shadow layers (not layout `border`).\n * - Navigation belongs to `ButtonLink`; an inline text link is an anchor.\n */\n\nconst COMMON_ROOT = cn(\n \"group relative inline-flex h-max cursor-pointer items-center justify-center whitespace-nowrap outline-pho-brand transition duration-100 ease-linear focus-visible:outline-2 focus-visible:outline-offset-2\",\n // Disabled (native `disabled` on <button>, `aria-disabled` on links).\n \"disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:pointer-events-none\",\n);\n\n/**\n * Size axis — padding, text size, and icon-only (svgOnly) padding.\n *\n * Icon-side tuck (LiftKit-derived optical correction): a side that carries an\n * icon gets its padding divided by the golden ratio — an icon's rounded form\n * brings its own whitespace, so the full text-edge padding reads as a hole.\n * LiftKit: `--button-padX-sideWithIcon: calc(font-size / 1.618)`. Applied to\n * Pho's paddings: sm 14px/φ ≈ 8px · md 16px/φ ≈ 10px · lg 20px/φ ≈ 12px,\n * selected structurally via `:has()` on the slot markers (`data-icon` is\n * `prefix`, `suffix`, or `loading` — the spinner is an icon too).\n */\nexport const BUTTON_SIZES = {\n sm: {\n classes: cn(\n \"gap-1 px-3.5 py-1.5 text-base font-medium data-svg-only:p-2\",\n \"has-[[data-icon=prefix]]:pl-2 has-[[data-icon=loading]]:pl-2 has-[[data-icon=suffix]]:pr-2\",\n ),\n description: \"Small — compact rows, tables, and toolbars\",\n },\n md: {\n classes: cn(\n \"gap-1 px-4 py-2 text-base font-medium data-svg-only:p-2.5\",\n \"has-[[data-icon=prefix]]:pl-2.5 has-[[data-icon=loading]]:pl-2.5 has-[[data-icon=suffix]]:pr-2.5\",\n ),\n description: \"Medium — the default\",\n },\n lg: {\n classes: cn(\n \"gap-1.5 px-5 py-2.5 text-md font-medium data-svg-only:p-3\",\n \"has-[[data-icon=prefix]]:pl-3 has-[[data-icon=loading]]:pl-3 has-[[data-icon=suffix]]:pr-3\",\n ),\n description: \"Large — prominent and marketing actions\",\n },\n} satisfies VariantMap;\n\n/** Shape axis — corner treatment. Pills are the photon.codes default. */\nexport const BUTTON_SHAPES = {\n rounded: {\n classes: \"rounded-full\",\n description: \"Pill — the default; every button on photon.codes\",\n },\n square: {\n classes: \"rounded-base\",\n description: \"Gently rounded corners for dense app chrome\",\n },\n circle: {\n classes: \"rounded-full\",\n description: \"Circular — pair with svgOnly for round icon buttons\",\n },\n} satisfies VariantMap;\n\n/**\n * At `lg`, square buttons add squircle smoothing on top of `rounded-base`\n * (12px) — the same treatment as Input `lg`, so 44px buttons and fields share\n * a corner language. 12/44 ≈ 0.272 — LiftKit's half-step decimal (√φ − 1).\n */\nconst SQUARE_LG_RADIUS = \"squircle\";\n\n/**\n * Optical pad for `effect`: the 1.5px outer rim is a box-shadow (no layout\n * cost), so without this the painted height reads ~3px taller than filled /\n * outlined / ghost. Subtract 1.5px from each side so fill+rim matches siblings.\n */\nconst EFFECT_OPTICAL_PAD: Record<ButtonSize, string> = {\n sm: \"py-[4.5px] data-svg-only:p-[6.5px]\",\n md: \"py-[6.5px] data-svg-only:p-[8.5px]\",\n lg: \"py-[8.5px] data-svg-only:p-[10.5px]\",\n};\n\n/** Surface axis — how the control is drawn. `effect` is the default. */\nexport const BUTTON_VARIANTS = {\n effect: {\n classes: \"\",\n description:\n \"Default — marketing pill with same-hue outer rim and inset sheen\",\n },\n filled: {\n classes: \"\",\n description: \"Solid fill without sheen\",\n },\n outlined: {\n classes: \"\",\n description: \"Hairline ring — supporting action\",\n },\n ghost: {\n classes: \"\",\n description: \"Borderless — quiet / inline action\",\n },\n} satisfies VariantMap;\n\n/** Color axis — semantic ink. `default` is the default. */\nexport const BUTTON_COLORS = {\n default: {\n classes: \"\",\n description: \"Neutral / brand contrast\",\n },\n destructive: {\n classes: \"\",\n description: \"Destructive intent (delete, discard)\",\n },\n} satisfies VariantMap;\n\n/**\n * Surface × color looks. Kept as a matrix so each cell can own its tokens\n * (filled destructive is solid red; ghost destructive is red text only).\n */\nconst BUTTON_LOOKS: Record<\n keyof typeof BUTTON_VARIANTS,\n Record<keyof typeof BUTTON_COLORS, string>\n> = {\n // Icons inherit the label ink (currentColor) — no separate opacity/tint.\n // The rim replicates photon.codes' Framer CTA border: 1.5px in the fill hue\n // at 62% alpha, reading as a soft same-hue edge (the site has NO outer\n // glow — verified against live computed styles). Sheen geometry is CSS\n // vars that Motion springs on hover (see Button). All edges are shadows —\n // never a layout `border` — so height stays paired with other variants.\n effect: {\n default: cn(\n \"squircle overflow-hidden bg-pho-brand-solid text-pho-on-brand transition-none\",\n \"hover:bg-pho-brand-solid data-loading:bg-pho-brand-solid\",\n \"[--pho-rim:color-mix(in_oklab,var(--background-color-pho-brand-solid)_62%,transparent)]\",\n \"[--pho-sheen:light-dark(rgba(255,255,255,0.5),rgba(0,0,0,0.12))]\",\n \"shadow-[0_0_0_1.5px_var(--pho-rim),inset_0_0_0_1px_var(--border-color-pho-on-brand),inset_0_var(--pho-sheen-y,0px)_var(--pho-sheen-blur,5px)_var(--pho-sheen-spread,2px)_var(--pho-sheen)]\",\n ),\n destructive: cn(\n \"squircle overflow-hidden bg-pho-error-solid text-white outline-pho-error transition-none\",\n \"hover:bg-pho-error-solid data-loading:bg-pho-error-solid\",\n \"[--pho-rim:color-mix(in_oklab,var(--background-color-pho-error-solid)_62%,transparent)]\",\n \"[--pho-sheen:light-dark(rgba(255,255,255,0.45),rgba(0,0,0,0.18))]\",\n \"shadow-[0_0_0_1.5px_var(--pho-rim),inset_0_0_0_1px_oklch(1_0_0/0.2),inset_0_var(--pho-sheen-y,0px)_var(--pho-sheen-blur,5px)_var(--pho-sheen-spread,2px)_var(--pho-sheen)]\",\n ),\n },\n filled: {\n default:\n \"bg-pho-brand-solid text-pho-on-brand hover:bg-pho-brand-solid-hover data-loading:bg-pho-brand-solid-hover\",\n destructive:\n \"bg-pho-error-solid text-white outline-pho-error hover:bg-pho-error-solid-hover data-loading:bg-pho-error-solid-hover\",\n },\n outlined: {\n default:\n \"bg-pho-primary text-pho-secondary ring-1 ring-pho-secondary ring-inset hover:bg-pho-primary-hover hover:text-pho-secondary-hover data-loading:bg-pho-primary-hover\",\n destructive:\n \"bg-pho-primary text-pho-error outline-pho-error ring-1 ring-pho-error ring-inset hover:bg-pho-error-primary hover:text-pho-error-hover data-loading:bg-pho-error-primary\",\n },\n ghost: {\n default:\n \"text-pho-secondary hover:bg-pho-ghost-hover hover:text-pho-secondary-hover data-loading:bg-pho-ghost-hover\",\n destructive:\n \"text-pho-error outline-pho-error hover:bg-pho-error-primary hover:text-pho-error-hover data-loading:bg-pho-error-primary\",\n },\n};\n\nexport type ButtonSize = keyof typeof BUTTON_SIZES;\nexport type ButtonShape = keyof typeof BUTTON_SHAPES;\nexport type ButtonVariant = keyof typeof BUTTON_VARIANTS;\nexport type ButtonColor = keyof typeof BUTTON_COLORS;\n\n/** Compose the full class string for a button — exported for reuse/testing. */\nexport function buttonVariants(opts?: {\n variant?: ButtonVariant;\n color?: ButtonColor;\n size?: ButtonSize;\n shape?: ButtonShape;\n className?: string;\n}): string {\n const {\n variant = \"effect\",\n color = \"default\",\n size = \"md\",\n shape: shapeOpt = \"rounded\",\n className,\n } = opts ?? {};\n // Effect pills are always fully rounded — shape is ignored.\n const shape = variant === \"effect\" ? \"rounded\" : shapeOpt;\n const look =\n BUTTON_LOOKS[variant as keyof typeof BUTTON_LOOKS]?.[\n color as keyof (typeof BUTTON_LOOKS)[\"effect\"]\n ] ?? \"\";\n return cn(\n COMMON_ROOT,\n resolveVariant(BUTTON_SHAPES, shape),\n shape === \"square\" && size === \"lg\" && SQUARE_LG_RADIUS,\n resolveVariant(BUTTON_SIZES, size),\n // Outer rim is a 1.5px box-shadow (no layout cost). Shrink padding by the\n // same amount so fill+rim paints at the same visual size as filled /\n // outlined / ghost siblings of the same `size`.\n variant === \"effect\" && EFFECT_OPTICAL_PAD[size],\n look,\n className,\n );\n}\n\n/** An icon/element slot rendered before or after the label. */\ntype Slottable = ReactNode | FC<{ className?: string }>;\n\ninterface ButtonBaseProps {\n /** Surface variant. @default \"effect\" */\n variant?: ButtonVariant;\n /** Semantic color. @default \"default\" */\n color?: ButtonColor;\n /** Size variant. @default \"md\" */\n size?: ButtonSize;\n /** Corner shape. @default \"rounded\" */\n shape?: ButtonShape;\n /** Slot before the label — a component (`Plus`) or element (`<Plus />`). */\n prefix?: Slottable;\n /** Slot after the label. */\n suffix?: Slottable;\n /**\n * Reveal prefix/suffix icons on hover — collapsed and invisible at rest,\n * spring in on hover. Independent of `variant` (including `effect`).\n * Ignored when `svgOnly`. @default false\n */\n iconReveal?: boolean;\n /** Render as an icon-only button. Requires an `aria-label`. */\n svgOnly?: boolean;\n /** Show a spinner and block interaction while staying readable. */\n loading?: boolean;\n children?: ReactNode;\n className?: string;\n}\n\n/**\n * DOM event handlers whose React signatures clash with Motion's gesture\n * props of the same name (Motion re-types these on `motion.*` elements).\n * Excluded from the public API — Button renders through Motion when the\n * sheen or icon reveal is active, so these handlers would be intercepted.\n */\ntype MotionHandlerConflicts =\n | \"onAnimationStart\"\n | \"onAnimationEnd\"\n | \"onAnimationIteration\"\n | \"onDrag\"\n | \"onDragStart\"\n | \"onDragEnd\"\n | \"onDragEnter\"\n | \"onDragExit\"\n | \"onDragLeave\"\n | \"onDragOver\"\n | \"onDrop\";\n\nexport interface ButtonProps\n extends\n ButtonBaseProps,\n Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n keyof ButtonBaseProps | MotionHandlerConflicts\n > {\n ref?: Ref<HTMLButtonElement>;\n}\n\nexport interface ButtonLinkProps\n extends\n ButtonBaseProps,\n Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n keyof ButtonBaseProps | MotionHandlerConflicts\n > {\n /** Navigate to this URL (rendered via the host's `LinkProvider`). */\n href: string;\n /** Visually and semantically disable the link. */\n disabled?: boolean;\n}\n\nconst SLOT_CLASS =\n // Icons scale with the label: 1.125em tracks the button's font size\n // (16px icon beside 14px text, 18px beside 16px) so small buttons never\n // carry an oversized glyph.\n \"transition-inherit-all pointer-events-none inline-flex shrink-0 items-center [&>svg]:size-[1.125em] [&>svg]:shrink-0\";\n\nfunction Slot({\n position,\n children,\n}: {\n position: \"prefix\" | \"suffix\" | \"loading\";\n children: ReactNode;\n}) {\n return (\n <span data-icon={position} className={SLOT_CLASS}>\n {children}\n </span>\n );\n}\n\nfunction renderSlot(icon: Slottable, position: \"prefix\" | \"suffix\") {\n if (icon == null || icon === false) return null;\n if (isReactComponent(icon)) {\n const Icon = icon;\n return (\n <Slot position={position}>\n <Icon />\n </Slot>\n );\n }\n if (isValidElement(icon)) return <Slot position={position}>{icon}</Slot>;\n return null;\n}\n\nfunction warnMissingLabel(svgOnly: boolean | undefined, label: unknown) {\n if (\n svgOnly &&\n !label &&\n typeof process !== \"undefined\" &&\n process.env?.NODE_ENV !== \"production\"\n ) {\n console.warn(\n \"Button: `svgOnly` buttons require an `aria-label` that names the action.\",\n );\n }\n}\n\n/* ======================== Motion: sheen + icon reveal ======================== */\n\n/** Framer source transition: `spring-physics 200 30 0.1`. */\nconst BUTTON_SPRING = {\n type: \"spring\",\n stiffness: 200,\n damping: 30,\n mass: 0.1,\n} as const;\n\nconst EFFECT_SHEEN_REST = {\n \"--pho-sheen-y\": \"0px\",\n \"--pho-sheen-blur\": \"5px\",\n \"--pho-sheen-spread\": \"2px\",\n} as const;\n\n/** Per-size sheen hover targets. */\nconst EFFECT_SHEEN_HOVER = {\n sm: {\n \"--pho-sheen-y\": \"-3px\",\n \"--pho-sheen-blur\": \"8px\",\n \"--pho-sheen-spread\": \"2px\",\n },\n md: {\n \"--pho-sheen-y\": \"-3px\",\n \"--pho-sheen-blur\": \"8px\",\n \"--pho-sheen-spread\": \"2px\",\n },\n lg: {\n \"--pho-sheen-y\": \"-6px\",\n \"--pho-sheen-blur\": \"14px\",\n \"--pho-sheen-spread\": \"1px\",\n },\n} as const;\n\n/**\n * Per-size sheen press targets — the inverse gesture of hover: the light\n * drops back below the rest line and tightens while the pill compresses\n * slightly, so hover → press reads as \"lift, then push in\".\n */\nconst EFFECT_SHEEN_PRESS = {\n sm: {\n \"--pho-sheen-y\": \"1.5px\",\n \"--pho-sheen-blur\": \"4px\",\n \"--pho-sheen-spread\": \"1px\",\n },\n md: {\n \"--pho-sheen-y\": \"1.5px\",\n \"--pho-sheen-blur\": \"4px\",\n \"--pho-sheen-spread\": \"1px\",\n },\n lg: {\n \"--pho-sheen-y\": \"3px\",\n \"--pho-sheen-blur\": \"7px\",\n \"--pho-sheen-spread\": \"1px\",\n },\n} as const;\n\n/** Pill compression while pressed — subtle, transform-only. */\nconst EFFECT_PRESS_SCALE = 0.97;\n\n/**\n * Icon reveal sizes — collapsed at rest, full on hover. `lg` matches the\n * Framer 20×21 source; `sm`/`md` scale with the 14px label.\n */\nconst ICON_REVEAL_VARIANTS = {\n sm: {\n rest: { opacity: 0, width: 1, height: 9 },\n hover: { opacity: 1, width: 17, height: 18 },\n },\n md: {\n rest: { opacity: 0, width: 1, height: 9 },\n hover: { opacity: 1, width: 17, height: 18 },\n },\n lg: {\n rest: { opacity: 0, width: 1, height: 10 },\n hover: { opacity: 1, width: 20, height: 21 },\n },\n} as const;\n\n/**\n * When icons are hidden at rest, undo Button's icon-side padding tuck so the\n * pill doesn't look lopsided. Same `:has()` keys as the tuck (later wins).\n */\nconst ICON_REVEAL_PAD_RESTORE: Record<ButtonSize, string> = {\n sm: \"has-[[data-icon=prefix]]:pl-3.5 has-[[data-icon=loading]]:pl-3.5 has-[[data-icon=suffix]]:pr-3.5\",\n md: \"has-[[data-icon=prefix]]:pl-4 has-[[data-icon=loading]]:pl-4 has-[[data-icon=suffix]]:pr-4\",\n lg: \"has-[[data-icon=prefix]]:pl-5 has-[[data-icon=loading]]:pl-5 has-[[data-icon=suffix]]:pr-5\",\n};\n\nfunction RevealSlot({\n position,\n size,\n children,\n}: {\n position: \"prefix\" | \"suffix\";\n size: ButtonSize;\n children: ReactNode;\n}) {\n return (\n <motion.span\n data-icon={position}\n variants={ICON_REVEAL_VARIANTS[size]}\n transition={BUTTON_SPRING}\n style={{\n display: \"inline-flex\",\n flex: \"none\",\n alignItems: \"center\",\n overflow: \"hidden\",\n }}\n className=\"pointer-events-none [&>svg]:block [&>svg]:size-full\"\n >\n {children}\n </motion.span>\n );\n}\n\nfunction wrapRevealIcon(\n icon: Slottable,\n position: \"prefix\" | \"suffix\",\n size: ButtonSize,\n): ReactNode | undefined {\n if (icon == null || icon === false) return undefined;\n if (isReactComponent(icon)) {\n const Icon = icon;\n return (\n <RevealSlot position={position} size={size}>\n <Icon />\n </RevealSlot>\n );\n }\n if (isValidElement(icon)) {\n return (\n <RevealSlot position={position} size={size}>\n {icon}\n </RevealSlot>\n );\n }\n return (\n <RevealSlot position={position} size={size}>\n {icon}\n </RevealSlot>\n );\n}\n\nfunction buttonInner({\n svgOnly,\n loading,\n prefix,\n suffix,\n children,\n iconReveal,\n size,\n}: Pick<\n ButtonBaseProps,\n | \"svgOnly\"\n | \"loading\"\n | \"prefix\"\n | \"suffix\"\n | \"children\"\n | \"iconReveal\"\n | \"size\"\n> & { size: ButtonSize }) {\n if (svgOnly) {\n return loading ? <Spinner /> : children;\n }\n const reveal = Boolean(iconReveal);\n return (\n <>\n {loading ? (\n <Slot position=\"loading\">\n <Spinner />\n </Slot>\n ) : reveal ? (\n wrapRevealIcon(prefix, \"prefix\", size)\n ) : (\n renderSlot(prefix, \"prefix\")\n )}\n {children != null && children !== false && (\n <span data-text className=\"transition-inherit-all px-0.5\">\n {children}\n </span>\n )}\n {reveal\n ? wrapRevealIcon(suffix, \"suffix\", size)\n : renderSlot(suffix, \"suffix\")}\n </>\n );\n}\n\n/** Root Motion props for sheen and/or icon-reveal gap. */\nfunction buttonRootMotionProps(\n size: ButtonSize,\n inert: boolean | undefined,\n opts: { sheen: boolean; iconReveal: boolean },\n) {\n const rest: Record<string, string | number> = opts.sheen\n ? { ...EFFECT_SHEEN_REST, scale: 1 }\n : {};\n const hover: Record<string, string | number> = opts.sheen\n ? { ...EFFECT_SHEEN_HOVER[size] }\n : {};\n // Press composes over hover (whileTap > whileHover): the sheen inverts and\n // the pill compresses; the icon-reveal gap keeps its hover value.\n const press: Record<string, string | number> = opts.sheen\n ? { ...EFFECT_SHEEN_PRESS[size], scale: EFFECT_PRESS_SCALE }\n : {};\n if (opts.iconReveal) {\n // Collapse gap at rest so the hidden icon leaves no hole; lg eases open.\n rest.gap = 0;\n hover.gap = size === \"lg\" ? 4 : 0;\n }\n return {\n initial: false as const,\n animate: \"rest\",\n whileHover: inert ? undefined : \"hover\",\n whileTap: inert || !opts.sheen ? undefined : \"press\",\n variants: { rest, hover, press },\n transition: BUTTON_SPRING,\n };\n}\n\nfunction buttonClassName({\n variant,\n color,\n size,\n shape,\n loading,\n svgOnly,\n className,\n}: {\n variant?: ButtonVariant;\n color?: ButtonColor;\n size?: ButtonSize;\n shape?: ButtonShape;\n loading?: boolean;\n svgOnly?: boolean;\n className?: string;\n}) {\n return cn(\n buttonVariants({ variant, color, size, shape }),\n loading && \"pointer-events-none\",\n svgOnly && \"[&>svg]:size-[1.25em] [&>svg]:shrink-0\",\n className,\n );\n}\n\n/**\n * Button — triggers an action that mutates state (submit, save, delete).\n * For navigation that changes the URL, use {@link ButtonLink}.\n *\n * Default surface is `effect` (same-hue 1.5px outer rim + inset sheen). Use\n * `filled` for a solid fill without those layers. `iconReveal` springs\n * prefix/suffix icons in on hover — independent of variant.\n */\nexport function Button({\n variant = \"effect\",\n color,\n size = \"md\",\n shape,\n prefix,\n suffix,\n iconReveal = false,\n svgOnly,\n loading,\n children,\n className,\n ref,\n ...props\n}: ButtonProps) {\n warnMissingLabel(svgOnly, props[\"aria-label\"]);\n const sheen = variant === \"effect\";\n const reveal = iconReveal && !svgOnly;\n const cls = buttonClassName({\n variant,\n color,\n size,\n shape,\n loading,\n svgOnly,\n className: cn(reveal && ICON_REVEAL_PAD_RESTORE[size], className),\n });\n const inner = buttonInner({\n svgOnly,\n loading,\n prefix,\n suffix,\n children,\n iconReveal: reveal,\n size,\n });\n\n if (sheen || reveal) {\n return (\n <motion.button\n ref={ref}\n type=\"button\"\n {...buttonRootMotionProps(size, props.disabled || loading, {\n sheen,\n iconReveal: reveal,\n })}\n {...props}\n aria-busy={loading || undefined}\n data-loading={loading ? \"true\" : undefined}\n data-svg-only={svgOnly ? \"true\" : undefined}\n className={cls}\n >\n {inner}\n </motion.button>\n );\n }\n\n return (\n <button\n ref={ref}\n type=\"button\"\n {...props}\n aria-busy={loading || undefined}\n data-loading={loading ? \"true\" : undefined}\n data-svg-only={svgOnly ? \"true\" : undefined}\n className={cls}\n >\n {inner}\n </button>\n );\n}\n\nButton.displayName = \"Button\";\n\n/**\n * ButtonLink — a link styled as a button, for navigation that changes the URL.\n * Shares every visual prop with {@link Button}; renders the host router's link\n * via `LinkProvider`.\n */\nexport function ButtonLink({\n variant = \"effect\",\n color,\n size = \"md\",\n shape,\n prefix,\n suffix,\n iconReveal = false,\n svgOnly,\n loading,\n disabled,\n href,\n children,\n className,\n ...props\n}: ButtonLinkProps) {\n warnMissingLabel(svgOnly, props[\"aria-label\"]);\n const Link = useLinkComponent();\n const sheen = variant === \"effect\";\n const reveal = iconReveal && !svgOnly;\n const cls = buttonClassName({\n variant,\n color,\n size,\n shape,\n loading,\n svgOnly,\n className: cn(reveal && ICON_REVEAL_PAD_RESTORE[size], className),\n });\n const inner = buttonInner({\n svgOnly,\n loading,\n prefix,\n suffix,\n children,\n iconReveal: reveal,\n size,\n });\n // Hooks must run unconditionally — only used when Motion is needed.\n const MotionLink = useMemo(\n // LinkProvider requires `href`; motion.create needs an open prop record —\n // bridge the two through `unknown` (props are supplied at the call site).\n () =>\n motion.create(Link as unknown as ComponentType<Record<string, unknown>>),\n [Link],\n );\n\n const inert = Boolean(disabled || loading);\n const shared = {\n ...props,\n ...(inert ? {} : { href }),\n \"aria-disabled\": inert || undefined,\n \"aria-busy\": loading || undefined,\n \"data-loading\": loading ? (\"true\" as const) : undefined,\n \"data-svg-only\": svgOnly ? (\"true\" as const) : undefined,\n className: cls,\n tabIndex: inert ? -1 : props.tabIndex,\n onClick: inert\n ? (event: ReactMouseEvent<HTMLAnchorElement>) => {\n event.preventDefault();\n }\n : props.onClick,\n };\n\n if (inert) {\n return <span {...shared}>{inner}</span>;\n }\n\n if (sheen || reveal) {\n return (\n <MotionLink\n {...buttonRootMotionProps(size, disabled || loading, {\n sheen,\n iconReveal: reveal,\n })}\n {...shared}\n >\n {inner}\n </MotionLink>\n );\n }\n\n return <Link {...shared}>{inner}</Link>;\n}\n\nButtonLink.displayName = \"ButtonLink\";\n\n/* ======================= EffectButton (compat alias) ======================= */\n\nexport const EFFECT_BUTTON_SIZES = BUTTON_SIZES;\nexport type EffectButtonSize = ButtonSize;\n\n/** Compose classes for an effect pill — `Button` with `variant=\"effect\"`. */\nexport function effectButtonVariants(opts?: {\n size?: EffectButtonSize;\n fullWidth?: boolean;\n className?: string;\n}): string {\n const { size = \"md\", fullWidth, className } = opts ?? {};\n return buttonVariants({\n variant: \"effect\",\n size,\n className: cn(fullWidth && \"w-full\", className),\n });\n}\n\n/**\n * Legacy EffectButton also reserved `style` (sheen variables lived there) and\n * never exposed the HTML `color` attribute — Button's semantic `color` prop\n * now occupies that name.\n */\ntype MotionConflicts = MotionHandlerConflicts | \"style\" | \"color\";\n\ninterface EffectButtonBaseProps {\n /** Size variant — Button's scale (32 / 36 / 44px). @default \"md\" */\n size?: EffectButtonSize;\n /** Stretch to the container's full width (stacked login / OAuth pills). */\n fullWidth?: boolean;\n /**\n * @deprecated Use `iconReveal` on `Button` instead. Mapped to `iconReveal`\n * when set.\n */\n animated?: boolean;\n /** @see ButtonBaseProps.iconReveal */\n iconReveal?: boolean;\n prefix?: Slottable;\n suffix?: Slottable;\n svgOnly?: boolean;\n loading?: boolean;\n children?: ReactNode;\n className?: string;\n}\n\nexport interface EffectButtonProps\n extends\n EffectButtonBaseProps,\n Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n keyof EffectButtonBaseProps | MotionConflicts\n > {\n ref?: Ref<HTMLButtonElement>;\n}\n\nexport interface EffectButtonLinkProps\n extends\n EffectButtonBaseProps,\n Omit<\n AnchorHTMLAttributes<HTMLAnchorElement>,\n keyof EffectButtonBaseProps | MotionConflicts\n > {\n href: string;\n disabled?: boolean;\n}\n\n/**\n * @deprecated Prefer `<Button variant=\"effect\">`. Thin alias that forwards to\n * Button with the sheen surface (no icon reveal).\n */\nexport function EffectButton({\n size = \"md\",\n fullWidth,\n animated,\n iconReveal,\n className,\n ...props\n}: EffectButtonProps) {\n return (\n <Button\n variant=\"effect\"\n size={size}\n iconReveal={iconReveal ?? animated ?? true}\n className={cn(fullWidth && \"w-full\", className)}\n {...props}\n />\n );\n}\n\nEffectButton.displayName = \"EffectButton\";\n\n/**\n * @deprecated Prefer `<ButtonLink variant=\"effect\">`.\n */\nexport function EffectButtonLink({\n size = \"md\",\n fullWidth,\n animated,\n iconReveal,\n className,\n ...props\n}: EffectButtonLinkProps) {\n return (\n <ButtonLink\n variant=\"effect\"\n size={size}\n iconReveal={iconReveal ?? animated ?? true}\n className={cn(fullWidth && \"w-full\", className)}\n {...props}\n />\n );\n}\n\nEffectButtonLink.displayName = \"EffectButtonLink\";\n","import { type ButtonHTMLAttributes } from \"react\";\nimport { IconX } from \"@tabler/icons-react\";\nimport { cn } from \"../../utils/cn\";\nimport { resolveVariant, type VariantMap } from \"../../utils/resolve-variant\";\n\n// `rounded-base` (12px) at every size; base classes already apply squircle.\nconst CLOSE_SIZES = {\n sm: { classes: \"size-9 rounded-base *:size-5\" },\n md: { classes: \"size-10 rounded-base *:size-5\" },\n lg: { classes: \"size-11 rounded-base *:size-6\" },\n} satisfies VariantMap;\n\nexport interface CloseButtonProps extends Omit<\n ButtonHTMLAttributes<HTMLButtonElement>,\n \"color\"\n> {\n /** Size variant. @default \"sm\" */\n size?: keyof typeof CLOSE_SIZES;\n /** Accessible label. @default \"Close\" */\n label?: string;\n}\n\n/**\n * Icon-only dismiss control — a squircle chip that fills on hover. Adapts to\n * light/dark automatically via semantic tokens. Used by Dialog and dismissible\n * surfaces.\n */\nexport function CloseButton({\n size = \"sm\",\n label,\n className,\n ...props\n}: CloseButtonProps) {\n return (\n <button\n type=\"button\"\n aria-label={label || \"Close\"}\n {...props}\n className={cn(\n \"squircle text-pho-secondary outline-pho-brand hover:bg-pho-ghost-hover hover:text-pho-secondary-hover inline-flex cursor-pointer items-center justify-center p-2 transition duration-100 ease-linear focus-visible:outline-2 focus-visible:outline-offset-2\",\n resolveVariant(CLOSE_SIZES, size),\n className,\n )}\n >\n <IconX aria-hidden=\"true\" className=\"transition-inherit-all shrink-0\" />\n </button>\n );\n}\n\nCloseButton.displayName = \"CloseButton\";\n"],"mappings":"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"}
@@ -1747,9 +1747,11 @@ var $e = (e, t) => {
1747
1747
  }, Tr = (e, ...t) => typeof e == "function" ? ye(Ge, e, ...t) : ye(() => Pr(Ge(), e), ...t), Lr = Tr({ extend: { theme: { text: [
1748
1748
  "display-xs",
1749
1749
  "display-sm",
1750
+ "display-base",
1750
1751
  "display-md",
1751
1752
  "display-lg",
1752
- "display-xl"
1753
+ "display-xl",
1754
+ "display-2xl"
1753
1755
  ] } } });
1754
1756
  function Nr(...e) {
1755
1757
  return Lr(Be(e));
@@ -1758,4 +1760,4 @@ export {
1758
1760
  Nr as t
1759
1761
  };
1760
1762
 
1761
- //# sourceMappingURL=cn-7uGUzCOM.js.map
1763
+ //# sourceMappingURL=cn-DTiB6ek_.js.map