@photon-ai/pho-ui 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/chunks/basic-page-C50WUzYV.js +238 -0
  2. package/dist/chunks/basic-page-C50WUzYV.js.map +1 -0
  3. package/dist/chunks/card-9cGnv-VD.js.map +1 -1
  4. package/dist/chunks/{close-button-Bq9f-H15.js → close-button-B8gF5UNk.js} +3 -3
  5. package/dist/chunks/close-button-B8gF5UNk.js.map +1 -0
  6. package/dist/chunks/dialog-Ba0UUE5g.js +155 -0
  7. package/dist/chunks/dialog-Ba0UUE5g.js.map +1 -0
  8. package/dist/chunks/{sidebar-BCqq58hV.js → sidebar-DGpEKxOn.js} +3 -3
  9. package/dist/chunks/{sidebar-BCqq58hV.js.map → sidebar-DGpEKxOn.js.map} +1 -1
  10. package/dist/chunks/use-press-pulse-8iorQPsh.js +62 -0
  11. package/dist/chunks/use-press-pulse-8iorQPsh.js.map +1 -0
  12. package/dist/components/badge.js +46 -31
  13. package/dist/components/badge.js.map +1 -1
  14. package/dist/components/button.js +1 -1
  15. package/dist/components/checkbox.js +1 -1
  16. package/dist/components/dialog.js +1 -1
  17. package/dist/components/select.js +1 -1
  18. package/dist/index.js +36 -36
  19. package/dist/sections/basic-page.js +8 -0
  20. package/dist/sections/sidebar.js +1 -1
  21. package/dist/src/components/badge/badge.d.ts +34 -8
  22. package/dist/src/components/badge/index.d.ts +1 -1
  23. package/dist/src/components/card/card.d.ts +2 -2
  24. package/dist/src/components/dialog/dialog.d.ts +16 -1
  25. package/dist/src/index.d.ts +1 -1
  26. package/dist/src/sections/basic-page/basic-page.d.ts +270 -0
  27. package/dist/src/sections/basic-page/index.d.ts +1 -0
  28. package/dist/src/sections/sidebar/sidebar.d.ts +1 -1
  29. package/dist/src/utils/use-press-pulse.d.ts +15 -11
  30. package/dist/utils.js +1 -1
  31. package/package.json +4 -4
  32. package/src/styles/theme.css +20 -0
  33. package/dist/chunks/close-button-Bq9f-H15.js.map +0 -1
  34. package/dist/chunks/dialog-DolPKFGS.js +0 -141
  35. package/dist/chunks/dialog-DolPKFGS.js.map +0 -1
  36. package/dist/chunks/settings-B0vhLoMi.js +0 -134
  37. package/dist/chunks/settings-B0vhLoMi.js.map +0 -1
  38. package/dist/chunks/use-press-pulse-CMwPTOR2.js +0 -44
  39. package/dist/chunks/use-press-pulse-CMwPTOR2.js.map +0 -1
  40. package/dist/sections/settings.js +0 -8
  41. package/dist/src/sections/settings/index.d.ts +0 -1
  42. package/dist/src/sections/settings/settings.d.ts +0 -170
@@ -48,8 +48,8 @@ declare function Footer({ className, ...props }: ComponentProps<"div"> & {
48
48
  /**
49
49
  * Card — a bordered content panel: header (title + description), body,
50
50
  * optional footer. It's static chrome with no interactivity of its own.
51
- * For a settings label/control list use `Settings.Card`; for a featured
52
- * block on the page surface use `Settings.Callout`.
51
+ * For a label/control list use `BasicPage.Card`; for a featured
52
+ * block on the page surface use `BasicPage.Callout`.
53
53
  *
54
54
  * ```tsx
55
55
  * <Card.Root>
@@ -44,6 +44,21 @@ interface ContentProps extends Omit<StyledProps<typeof BaseDialog.Popup>, "title
44
44
  title?: ReactNode;
45
45
  /** Supporting text under the title. */
46
46
  description?: ReactNode;
47
+ /**
48
+ * Consequence bullets rendered as a list at the top of the body — one item
49
+ * per outcome (what is deleted, what survives, who loses access). Use on
50
+ * destructive confirms where the one-line description undersells the
51
+ * impact; keep each item to a single short sentence. Wrap the load-bearing
52
+ * figure in `<strong>` ("<strong>3 members</strong> lose access") — the
53
+ * list styles it (secondary ink, medium weight); no classes needed.
54
+ */
55
+ consequences?: ReactNode[];
56
+ /**
57
+ * Final cautionary line under `consequences` — error ink at description
58
+ * size (e.g. "It cannot be undone. Please be certain."). Reserve it for
59
+ * irreversible actions; keep it short.
60
+ */
61
+ warning?: ReactNode;
47
62
  /**
48
63
  * Footer action row (Cancel / Confirm, etc.). Iron rule: when `actions` is
49
64
  * set, the header close button is never shown — dismiss via the actions.
@@ -62,7 +77,7 @@ interface ContentProps extends Omit<StyledProps<typeof BaseDialog.Popup>, "title
62
77
  * full control, compose the parts directly (`Dialog.Portal` /
63
78
  * `Dialog.Backdrop` / `Dialog.Popup`).
64
79
  */
65
- declare function Content({ title, description, actions, showClose, children, className, ...props }: ContentProps): import("react").JSX.Element;
80
+ declare function Content({ title, description, consequences, warning, actions, showClose, children, className, ...props }: ContentProps): import("react").JSX.Element;
66
81
  /**
67
82
  * Modal dialog built on Base UI `Dialog`. Controlled or uncontrolled via
68
83
  * `Dialog.Root` (`open` / `onOpenChange` / `defaultOpen`). `Dialog.Action`
@@ -12,4 +12,4 @@ export * from './components/input-group';
12
12
  export * from './components/card';
13
13
  export * from './components/dialog';
14
14
  export * from './sections/sidebar';
15
- export * from './sections/settings';
15
+ export * from './sections/basic-page';
@@ -0,0 +1,270 @@
1
+ import { AnchorHTMLAttributes, ComponentProps, ReactNode, Ref } from 'react';
2
+ import { ButtonLinkProps } from '../../components/button';
3
+ /**
4
+ * BasicPage — Pho's standard content page: a page title, section titles, and
5
+ * bordered cards of label/control rows. Settings, billing, members — any
6
+ * single-column management surface.
7
+ *
8
+ * Spacing: wrap the page in `Root` (a 42rem column with `px-5` gutters,
9
+ * vertical padding). `Stack` separates blocks (`gap-10`). Inside a block,
10
+ * children share `gap-3`. Wrap a section title + description in `Header`
11
+ * (`gap-0.5`) so that pair stays tight — don't put them as loose siblings.
12
+ * Parts already carry `px-5`.
13
+ *
14
+ * Navigation: pages may drill into subpages. `RowLink` is a whole-row link
15
+ * (trailing chevron) and `Back` returns to the parent — both render through
16
+ * the host's `LinkProvider`; the URL itself stays the router's job. `Back`
17
+ * floats in `Root`'s top padding, so `Title` never moves between pages that
18
+ * have a back link and pages that don't.
19
+ */
20
+ /** Bordered card that stacks rows with hairline dividers. */
21
+ export declare const BASIC_PAGE_CARD = "divide-y divide-pho-secondary rounded-base border border-pho-secondary bg-pho-primary";
22
+ /**
23
+ * Standalone callout surface — featured block (invite form, notice, etc.).
24
+ * Don't wrap in `Card`; this is the outer chrome.
25
+ */
26
+ export declare const BASIC_PAGE_CALLOUT = "flex flex-col gap-3 rounded-base border border-pho-secondary bg-pho-page p-5";
27
+ /**
28
+ * One row — label (+ optional description) left, control right.
29
+ * `min-h-[4.25rem]` (68px) keeps avatar/control rows from collapsing shorter
30
+ * than a comfortable touch target.
31
+ */
32
+ export declare const BASIC_PAGE_ROW = "flex min-h-[4.25rem] flex-col gap-3 px-5 py-4 sm:flex-row sm:items-center sm:justify-between sm:gap-6";
33
+ /**
34
+ * Page chrome — centered column at the standard content width. Hosts
35
+ * `Title` + `Stack` (and any route-level siblings). Prefer this over
36
+ * ad-hoc `max-w-*` wrappers in the app shell.
37
+ *
38
+ * `44.5rem` = the 42rem (`2xl`) content column plus the two `px-5` gutters,
39
+ * so wide viewports render exactly as before while narrow ones keep a 20px
40
+ * breathing edge instead of running content into the viewport.
41
+ *
42
+ * `relative` anchors `Back`, which floats in the top padding — the reserved
43
+ * zone that keeps `Title` at the same position on every page.
44
+ */
45
+ declare function Root({ className, ...props }: ComponentProps<"div"> & {
46
+ ref?: Ref<HTMLDivElement>;
47
+ }): import("react").JSX.Element;
48
+ /**
49
+ * Page title — largest heading on the surface. Plain-text children get a slug
50
+ * `id` for hash targets (`#profile`); the heading itself is not a link.
51
+ */
52
+ declare function Title({ id, className, children, ...props }: ComponentProps<"h1"> & {
53
+ ref?: Ref<HTMLHeadingElement>;
54
+ }): import("react").JSX.Element;
55
+ /**
56
+ * Vertical stack under the page title. Blocks (forms / sections) use `gap-10`;
57
+ * each block lays out its children with `gap-3`.
58
+ */
59
+ declare function Stack({ className, ...props }: ComponentProps<"div"> & {
60
+ ref?: Ref<HTMLDivElement>;
61
+ }): import("react").JSX.Element;
62
+ /**
63
+ * Title + optional description pair. Keeps that couple tight (`gap-0.5`) so
64
+ * the block's `gap-3` only applies between this header and the card.
65
+ */
66
+ declare function Header({ className, ...props }: ComponentProps<"div"> & {
67
+ ref?: Ref<HTMLDivElement>;
68
+ }): import("react").JSX.Element;
69
+ /**
70
+ * Section heading — same padding as the page title, smaller type. Hashable via
71
+ * slug `id` (not a link).
72
+ */
73
+ declare function SectionTitle({ id, className, children, ...props }: ComponentProps<"h2"> & {
74
+ ref?: Ref<HTMLHeadingElement>;
75
+ }): import("react").JSX.Element;
76
+ /**
77
+ * Supporting copy under a section title. Same display step as the title;
78
+ * color + normal weight keep it secondary. Always wrap with `Header`.
79
+ */
80
+ declare function Description({ className, ...props }: ComponentProps<"p"> & {
81
+ ref?: Ref<HTMLParagraphElement>;
82
+ }): import("react").JSX.Element;
83
+ /**
84
+ * Optional caption below a section's `Card` (outside the card). One step
85
+ * smaller than `Description` (`text-xs`). Not the card-internal dirty-state
86
+ * `Footer`.
87
+ */
88
+ declare function SectionFooter({ className, ...props }: ComponentProps<"p"> & {
89
+ ref?: Ref<HTMLParagraphElement>;
90
+ }): import("react").JSX.Element;
91
+ /** Bordered card that stacks rows with hairline dividers. */
92
+ declare function Card({ className, ...props }: ComponentProps<"div"> & {
93
+ ref?: Ref<HTMLDivElement>;
94
+ }): import("react").JSX.Element;
95
+ /**
96
+ * Standalone callout. Pair with `CalloutTitle` / `CalloutDescription` (and
97
+ * optional actions) for a featured block — e.g. an invite form. Do not wrap
98
+ * in `Card`; this is already the outer surface.
99
+ */
100
+ declare function Callout({ className, ...props }: ComponentProps<"div"> & {
101
+ ref?: Ref<HTMLDivElement>;
102
+ }): import("react").JSX.Element;
103
+ /** Callout heading — same type as `SectionTitle`. Hashable via slug `id`. */
104
+ declare function CalloutTitle({ id, className, children, ...props }: ComponentProps<"h2"> & {
105
+ ref?: Ref<HTMLHeadingElement>;
106
+ }): import("react").JSX.Element;
107
+ /** Supporting copy under a callout title. */
108
+ declare function CalloutDescription({ className, ...props }: ComponentProps<"p"> & {
109
+ ref?: Ref<HTMLParagraphElement>;
110
+ }): import("react").JSX.Element;
111
+ export interface BasicPageRowProps extends Omit<ComponentProps<"div">, "title"> {
112
+ /** Left-side field name. */
113
+ label: string;
114
+ /** Optional helper under the label. */
115
+ description?: ReactNode;
116
+ /** Associates the label with a control id. When omitted, the label is not a `<label>`. */
117
+ htmlFor?: string;
118
+ ref?: Ref<HTMLDivElement>;
119
+ }
120
+ /**
121
+ * One row: label (+ optional description) left, control right.
122
+ * Pass `htmlFor` when the row wraps a labelled input; omit it for display/
123
+ * action rows (avatar, danger-zone delete).
124
+ */
125
+ declare function Row({ label, description, htmlFor, className, children, ...props }: BasicPageRowProps): import("react").JSX.Element;
126
+ /** Read-only value on the right side of a row (email, etc.). */
127
+ declare function Value({ className, ...props }: ComponentProps<"p"> & {
128
+ ref?: Ref<HTMLParagraphElement>;
129
+ }): import("react").JSX.Element;
130
+ /**
131
+ * Inline error under a row (still inside the card). Matches card horizontal
132
+ * padding so hosts don't re-apply `px-5`.
133
+ */
134
+ declare function Alert({ className, ...props }: ComponentProps<"p"> & {
135
+ ref?: Ref<HTMLParagraphElement>;
136
+ }): import("react").JSX.Element;
137
+ export interface BasicPageFooterProps extends ComponentProps<"div"> {
138
+ /**
139
+ * Animated presence for dirty-state bars: the card glides open/closed on a
140
+ * height spring while the content fades. Omit for a static, always-visible
141
+ * footer (no animation). No reveal plays on first mount — only on flips.
142
+ */
143
+ show?: boolean;
144
+ ref?: Ref<HTMLDivElement>;
145
+ }
146
+ /**
147
+ * Trailing bar inside a card — typically a save status + submit button that
148
+ * appears after the form is dirty. Same horizontal/vertical padding as a row.
149
+ * Pass `show` (instead of conditional rendering) to animate the bar in and
150
+ * out following the onboarding card's motion language.
151
+ */
152
+ declare function Footer({ show, className, ...props }: BasicPageFooterProps): import("react").JSX.Element;
153
+ export interface BasicPageBackProps extends Omit<ButtonLinkProps, "variant" | "size" | "shape" | "prefix" | "suffix" | "svgOnly" | "href"> {
154
+ /** Parent destination — rendered through the host's `LinkProvider`. */
155
+ href: string;
156
+ /** Label — name the parent page. @default "Back" */
157
+ children?: ReactNode;
158
+ }
159
+ /**
160
+ * Back link for a subpage — a small ghost `ButtonLink`; place inside `Root`,
161
+ * before `Title`. It floats in `Root`'s reserved top padding (vertically
162
+ * centered in the 48px zone) instead of taking layout space, so `Title` sits
163
+ * at exactly the same position whether or not a page has one.
164
+ *
165
+ * The offsets align the *label* glyph with the title glyph. Title text sits
166
+ * 40px in (`px-5` root gutter + `px-5` title pad); inside a `sm` ghost button
167
+ * the label sits 29.75px in (8px icon-side pad + 15.75px chevron, 1.125em at
168
+ * the 14px `text-base`, + 4px gap + 2px label-span `px-0.5`) — so `left` is
169
+ * the 10.25px difference. `top-2` centers the 32px button in the 48px
170
+ * reserved zone. The chevron and the ghost hover pill hang in the padding —
171
+ * the usual optical alignment for borderless controls.
172
+ */
173
+ declare function Back({ href, className, children, ...props }: BasicPageBackProps): import("react").JSX.Element;
174
+ export interface BasicPageRowLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href" | "title"> {
175
+ /** Left-side field name — plain text, or compose with a trailing badge. */
176
+ label: ReactNode;
177
+ /** Optional helper under the label. */
178
+ description?: ReactNode;
179
+ /** Optional media left of the label (e.g. project avatar). */
180
+ leading?: ReactNode;
181
+ /** Subpage destination — rendered through the host's `LinkProvider`. */
182
+ href: string;
183
+ /** Optional trailing value before the chevron (compose with `Value`). */
184
+ children?: ReactNode;
185
+ ref?: Ref<HTMLAnchorElement>;
186
+ }
187
+ /**
188
+ * A row that navigates — the whole row is one link into a subpage, with a
189
+ * trailing chevron cue. Same geometry as `Row`; hover lifts the row, and
190
+ * first/last rows follow the card's corner radius.
191
+ */
192
+ declare function RowLink({ label, description, leading, href, className, children, ...props }: BasicPageRowLinkProps): import("react").JSX.Element;
193
+ /**
194
+ * Basic page layout. One stack, one gap rhythm — only title scale differs:
195
+ *
196
+ * ```tsx
197
+ * <BasicPage.Root>
198
+ * <BasicPage.Title>Profile</BasicPage.Title>
199
+ * <BasicPage.Stack>
200
+ * <form>
201
+ * <BasicPage.Card>
202
+ * <BasicPage.Row label="Name" htmlFor="first-name">
203
+ * <Input id="first-name" size="sm" />
204
+ * </BasicPage.Row>
205
+ * </BasicPage.Card>
206
+ * </form>
207
+ * <form>
208
+ * <BasicPage.Header>
209
+ * <BasicPage.SectionTitle>Agent profile</BasicPage.SectionTitle>
210
+ * <BasicPage.Description>…</BasicPage.Description>
211
+ * </BasicPage.Header>
212
+ * <BasicPage.Card>…</BasicPage.Card>
213
+ * </form>
214
+ * <div>
215
+ * <BasicPage.SectionTitle>Danger zone</BasicPage.SectionTitle>
216
+ * <BasicPage.Card>
217
+ * <BasicPage.Row label="Delete account" description="…">
218
+ * <Button color="destructive" size="sm">Delete</Button>
219
+ * </BasicPage.Row>
220
+ * </BasicPage.Card>
221
+ * </div>
222
+ * <BasicPage.Callout>
223
+ * <BasicPage.Header>
224
+ * <BasicPage.CalloutTitle>Invitations</BasicPage.CalloutTitle>
225
+ * <BasicPage.CalloutDescription>…</BasicPage.CalloutDescription>
226
+ * </BasicPage.Header>
227
+ * …invite form…
228
+ * </BasicPage.Callout>
229
+ * </BasicPage.Stack>
230
+ * </BasicPage.Root>
231
+ * ```
232
+ *
233
+ * Subpage — `Back` above the title, `RowLink` rows to drill in:
234
+ *
235
+ * ```tsx
236
+ * <BasicPage.Root>
237
+ * <BasicPage.Back href="/settings">Settings</BasicPage.Back>
238
+ * <BasicPage.Title>Billing</BasicPage.Title>
239
+ * <BasicPage.Stack>
240
+ * <div>
241
+ * <BasicPage.Card>
242
+ * <BasicPage.RowLink label="Invoices" href="/settings/invoices">
243
+ * <BasicPage.Value>12 documents</BasicPage.Value>
244
+ * </BasicPage.RowLink>
245
+ * </BasicPage.Card>
246
+ * </div>
247
+ * </BasicPage.Stack>
248
+ * </BasicPage.Root>
249
+ * ```
250
+ */
251
+ export declare const BasicPage: {
252
+ Root: typeof Root;
253
+ Back: typeof Back;
254
+ Title: typeof Title;
255
+ Stack: typeof Stack;
256
+ Header: typeof Header;
257
+ SectionTitle: typeof SectionTitle;
258
+ Description: typeof Description;
259
+ SectionFooter: typeof SectionFooter;
260
+ Card: typeof Card;
261
+ Callout: typeof Callout;
262
+ CalloutTitle: typeof CalloutTitle;
263
+ CalloutDescription: typeof CalloutDescription;
264
+ Row: typeof Row;
265
+ RowLink: typeof RowLink;
266
+ Value: typeof Value;
267
+ Alert: typeof Alert;
268
+ Footer: typeof Footer;
269
+ };
270
+ export {};
@@ -0,0 +1 @@
1
+ export { BasicPage, BASIC_PAGE_CARD, BASIC_PAGE_CALLOUT, BASIC_PAGE_ROW, type BasicPageRowProps, type BasicPageRowLinkProps, type BasicPageBackProps, type BasicPageFooterProps, } from './basic-page';
@@ -31,7 +31,7 @@ export declare const SIDEBAR_HEADER_INSET: {
31
31
  };
32
32
  export type SidebarInset = keyof typeof SIDEBAR_ROOT_INSET;
33
33
  /** A nav row at rest and on hover. Active/inactive inks split below. */
34
- export declare const SIDEBAR_ITEM = "outline-pho-brand flex min-h-8 w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-left text-base transition-colors focus-visible:outline-2 focus-visible:-outline-offset-1";
34
+ export declare const SIDEBAR_ITEM = "outline-pho-brand flex min-h-9 w-full cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-left text-base transition-colors focus-visible:outline-2 focus-visible:-outline-offset-1";
35
35
  /**
36
36
  * Selected row — lifted onto the primary surface with the site hairline.
37
37
  * The icon carries the same ink as the label so the pair reads as one unit.
@@ -1,11 +1,11 @@
1
1
  import { Transition } from 'motion/react';
2
2
  /**
3
- * Press pulse for selection controls (Select trigger, Checkbox…) — the same
4
- * spring family as Button's effect press, with one deliberate difference:
5
- * Button compresses only while held (`whileTap`); these controls play ONE
6
- * FULL pulse (press in → spring back) from the moment of pointer-down, even
7
- * if the pointer lifts early — a discrete "it took" tick, kept shallow and
8
- * quick so it registers without drawing attention.
3
+ * Press feedback for selection controls (Select trigger, Checkbox…) — the same
4
+ * spring family as Button's effect press, and the same pointer-following
5
+ * behavior: press in on pointer-down, stay compressed while held, spring back
6
+ * on release. One deliberate refinement over a bare `whileTap`: a fast tap
7
+ * never truncates the press-in leg — it always reaches full depth before
8
+ * springing back, so even the quickest click reads as a complete tick.
9
9
  */
10
10
  export declare const CONTROL_PRESS_SPRING: {
11
11
  readonly type: "spring";
@@ -13,17 +13,19 @@ export declare const CONTROL_PRESS_SPRING: {
13
13
  readonly damping: 32;
14
14
  readonly mass: 0.06;
15
15
  };
16
- /** Default pulse depth — shallow; each control can tune its own. */
16
+ /** Default press depth — shallow; each control can tune its own. */
17
17
  export declare const CONTROL_PRESS_SCALE = 0.96;
18
18
  /**
19
- * One-shot scale pulse driven by Motion controls. Spread `motionProps` onto a
20
- * `motion.*` element (via Base UI `render`): pointer-down (primary button)
21
- * plays press → rest in full; re-presses during the pulse are coalesced.
19
+ * Pointer-following press driven by Motion controls. Spread `motionProps` onto
20
+ * a `motion.*` element (via Base UI `render`): pointer-down (primary button)
21
+ * compresses and holds; release springs back once the press-in leg has
22
+ * finished. Release is tracked on `window`, so dragging off the control before
23
+ * letting go still restores it.
22
24
  */
23
25
  export declare function usePressPulse(opts?: {
24
26
  scale?: number;
25
27
  disabled?: boolean;
26
- /** Spring override for both legs of the pulse. @default CONTROL_PRESS_SPRING */
28
+ /** Spring override for both legs of the press. @default CONTROL_PRESS_SPRING */
27
29
  transition?: Transition;
28
30
  }): {
29
31
  pulse: () => void;
@@ -32,6 +34,8 @@ export declare function usePressPulse(opts?: {
32
34
  animate: import('motion/react').LegacyAnimationControls;
33
35
  onPointerDown: (event: {
34
36
  button: number;
37
+ pointerId: number;
38
+ isPrimary: boolean;
35
39
  }) => void;
36
40
  };
37
41
  };
package/dist/utils.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import { t as s } from "./chunks/cn-DTiB6ek_.js";
3
3
  import { n as t, r as e, t as a } from "./chunks/link-provider-9TFNyDqO.js";
4
4
  import { t as i } from "./chunks/resolve-variant-CcD8iG5l.js";
5
- import { n as p, r as P, t as R } from "./chunks/use-press-pulse-CMwPTOR2.js";
5
+ import { n as p, r as P, t as R } from "./chunks/use-press-pulse-8iorQPsh.js";
6
6
  export {
7
7
  R as CONTROL_PRESS_SCALE,
8
8
  p as CONTROL_PRESS_SPRING,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@photon-ai/pho-ui",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Pho Design System — Photon's React component library, built on Base UI",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -209,9 +209,9 @@
209
209
  "types": "./dist/src/sections/sidebar/index.d.ts",
210
210
  "import": "./dist/sections/sidebar.js"
211
211
  },
212
- "./sections/settings": {
213
- "types": "./dist/src/sections/settings/index.d.ts",
214
- "import": "./dist/sections/settings.js"
212
+ "./sections/basic-page": {
213
+ "types": "./dist/src/sections/basic-page/index.d.ts",
214
+ "import": "./dist/sections/basic-page.js"
215
215
  },
216
216
  "./icons": {
217
217
  "types": "./dist/src/icons/index.d.ts",
@@ -245,6 +245,20 @@
245
245
  oklch(0.619 0.152 149.3),
246
246
  oklch(0.72 0.162 149.5)
247
247
  );
248
+ /* Solid state fills — the state text inks reused as fills (high-contrast
249
+ Badge). Pair with text-pho-on-brand, which flips with them per mode. */
250
+ --background-color-pho-success-ink: light-dark(
251
+ oklch(0.549 0.136 149.3),
252
+ oklch(0.735 0.162 150.5)
253
+ );
254
+ --background-color-pho-warning-ink: light-dark(
255
+ oklch(0.572 0.125 69.8),
256
+ oklch(0.804 0.162 71.4)
257
+ );
258
+ --background-color-pho-error-ink: light-dark(
259
+ oklch(0.565 0.212 20.2),
260
+ oklch(0.696 0.198 14.9)
261
+ );
248
262
  --background-color-pho-error-primary: light-dark(
249
263
  oklch(0.642 0.245 21.5 / 0.08),
250
264
  oklch(0.642 0.245 21.5 / 0.15)
@@ -266,6 +280,9 @@
266
280
  oklch(0 0 0 / 0.15),
267
281
  oklch(1 0 0 / 0.15)
268
282
  );
283
+ /* Full primary ink — emphasized card edges that should read as solid
284
+ black/white, not the soft 15% primary hairline above. */
285
+ --border-color-pho-ink: light-dark(oklch(0 0 0), oklch(1 0 0));
269
286
  /* Hairline card stroke — the site's "BG/Card Stroke". */
270
287
  --border-color-pho-secondary: light-dark(oklch(0.949 0 0), oklch(0.244 0 0));
271
288
  --border-color-pho-tertiary: light-dark(
@@ -297,6 +314,9 @@
297
314
  oklch(0 0 0 / 0.15),
298
315
  oklch(1 0 0 / 0.15)
299
316
  );
317
+ /* Full primary ink — outlined control hover edges that should read as
318
+ solid black/white, not the soft 15% primary hairline above. */
319
+ --ring-color-pho-ink: light-dark(oklch(0 0 0), oklch(1 0 0));
300
320
  --ring-color-pho-secondary: light-dark(oklch(0.949 0 0), oklch(0.244 0 0));
301
321
  --ring-color-pho-brand: light-dark(oklch(0 0 0 / 0.4), oklch(1 0 0 / 0.4));
302
322
  --ring-color-pho-error: oklch(0.642 0.245 21.5);
@@ -1 +0,0 @@
1
- {"version":3,"file":"close-button-Bq9f-H15.js","names":[],"sources":["../../src/components/button/button.tsx","../../src/components/button/close-button.tsx"],"sourcesContent":["import {\n isValidElement,\n useEffect,\n useMemo,\n useRef,\n useState,\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, useReducedMotion } 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 * - Press scale (Motion `whileTap` → 0.97) is shared by every variant; only\n * `effect` also springs its inset sheen on hover/press, and replays a\n * one-shot sheen glint when it leaves `disabled`/`loading` (wake).\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 // Transition colors/shadows in CSS; transform (press scale) is owned by Motion.\n \"group relative inline-flex h-max cursor-pointer items-center justify-center whitespace-nowrap outline-pho-brand transition-[color,background-color,border-color,opacity,box-shadow,outline-color] 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; every variant. */\nconst BUTTON_PRESS_SCALE = 0.97;\n\n/**\n * One-shot \"wake\" glint when an effect pill re-enables (disabled / loading →\n * interactive). The keyframes replay the pill's own gesture in reverse: the\n * sheen starts from its pressed-in position (low, tight — the dormant read),\n * springs up past the hover line, and settles at rest. Shadow-only, so the\n * pill never moves or resizes next to its neighbours.\n */\nconst EFFECT_SHEEN_WAKE = {\n sm: {\n \"--pho-sheen-y\": [\"1.5px\", \"-4px\", \"0px\"],\n \"--pho-sheen-blur\": [\"4px\", \"9px\", \"5px\"],\n \"--pho-sheen-spread\": [\"1px\", \"2px\", \"2px\"],\n },\n md: {\n \"--pho-sheen-y\": [\"1.5px\", \"-4px\", \"0px\"],\n \"--pho-sheen-blur\": [\"4px\", \"9px\", \"5px\"],\n \"--pho-sheen-spread\": [\"1px\", \"2px\", \"2px\"],\n },\n lg: {\n \"--pho-sheen-y\": [\"3px\", \"-8px\", \"0px\"],\n \"--pho-sheen-blur\": [\"7px\", \"16px\", \"5px\"],\n \"--pho-sheen-spread\": [\"1px\", \"1px\", \"2px\"],\n },\n} as const;\n\n/**\n * Wake timing: a short delay lets the 100ms opacity fade land first, then a\n * quick rise (feedback) and a relaxed settle. Keyframes need a tween — the\n * shared spring only animates between two values.\n */\nconst WAKE_TRANSITION = {\n duration: 0.6,\n delay: 0.05,\n times: [0, 0.4, 1],\n ease: [\"easeOut\", \"easeInOut\"],\n} as const;\n\n/**\n * Fire the wake glint on the disabled/loading → enabled edge (never on\n * mount), skipped under reduced motion. `data-waking` mirrors the window so\n * tests and styling can observe it.\n */\nfunction useWakeOnEnable(inert: boolean, sheen: boolean) {\n const [waking, setWaking] = useState(false);\n const prevInert = useRef(inert);\n const reduceMotion = useReducedMotion();\n\n useEffect(() => {\n if (prevInert.current && !inert && sheen && !reduceMotion) {\n setWaking(true);\n } else if (inert) {\n setWaking(false);\n }\n prevInert.current = inert;\n }, [inert, sheen, reduceMotion]);\n\n return {\n waking,\n onWakeComplete: (definition: unknown) => {\n if (definition === \"wake\") setWaking(false);\n },\n };\n}\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/**\n * Root Motion props — press scale on every variant; sheen + icon-reveal gap\n * only when those features are on. Press composes over hover (`whileTap` >\n * `whileHover`): effect sheen inverts while the pill compresses; other\n * variants get the same scale with no sheen change. `waking` plays the\n * one-shot re-enable glint; a real hover mid-glint takes over seamlessly.\n */\nfunction buttonRootMotionProps(\n size: ButtonSize,\n inert: boolean | undefined,\n opts: { sheen: boolean; iconReveal: boolean; waking?: boolean },\n) {\n const rest: Record<string, string | number> = {\n scale: 1,\n ...(opts.sheen ? EFFECT_SHEEN_REST : {}),\n };\n const hover: Record<string, string | number> = opts.sheen\n ? { ...EFFECT_SHEEN_HOVER[size] }\n : {};\n const press: Record<string, string | number> = {\n scale: BUTTON_PRESS_SCALE,\n ...(opts.sheen ? EFFECT_SHEEN_PRESS[size] : {}),\n };\n const wake = opts.sheen\n ? { ...EFFECT_SHEEN_WAKE[size], transition: WAKE_TRANSITION }\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: opts.waking && opts.sheen ? \"wake\" : \"rest\",\n whileHover: inert ? undefined : \"hover\",\n whileTap: inert ? undefined : \"press\",\n variants: { rest, hover, press, wake },\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 inert = Boolean(props.disabled || loading);\n const { waking, onWakeComplete } = useWakeOnEnable(inert, sheen);\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 return (\n <motion.button\n ref={ref}\n type=\"button\"\n {...buttonRootMotionProps(size, inert, {\n sheen,\n iconReveal: reveal,\n waking,\n })}\n onAnimationComplete={onWakeComplete}\n {...props}\n aria-busy={loading || undefined}\n data-loading={loading ? \"true\" : undefined}\n data-svg-only={svgOnly ? \"true\" : undefined}\n data-waking={waking || undefined}\n className={cls}\n >\n {inner}\n </motion.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 { waking, onWakeComplete } = useWakeOnEnable(inert, sheen);\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 return (\n <MotionLink\n {...buttonRootMotionProps(size, inert, {\n sheen,\n iconReveal: reveal,\n waking,\n })}\n onAnimationComplete={onWakeComplete}\n data-waking={waking || undefined}\n {...shared}\n >\n {inner}\n </MotionLink>\n );\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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"}