@photon-ai/pho-ui 1.2.1 → 1.4.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 (39) hide show
  1. package/dist/chunks/basic-page-4_GZOvRr.js +255 -0
  2. package/dist/chunks/basic-page-4_GZOvRr.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/status-view-CcbctPqR.js +65 -0
  11. package/dist/chunks/status-view-CcbctPqR.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/dialog.js +1 -1
  16. package/dist/components/status-view.js +8 -0
  17. package/dist/index.js +55 -47
  18. package/dist/sections/basic-page.js +11 -0
  19. package/dist/sections/sidebar.js +1 -1
  20. package/dist/src/components/badge/badge.d.ts +34 -8
  21. package/dist/src/components/badge/index.d.ts +1 -1
  22. package/dist/src/components/card/card.d.ts +2 -2
  23. package/dist/src/components/dialog/dialog.d.ts +16 -1
  24. package/dist/src/components/status-view/index.d.ts +1 -0
  25. package/dist/src/components/status-view/status-view.d.ts +74 -0
  26. package/dist/src/index.d.ts +2 -1
  27. package/dist/src/sections/basic-page/basic-page.d.ts +332 -0
  28. package/dist/src/sections/basic-page/index.d.ts +1 -0
  29. package/dist/src/sections/sidebar/sidebar.d.ts +1 -1
  30. package/package.json +8 -4
  31. package/src/styles/theme.css +20 -0
  32. package/dist/chunks/close-button-Bq9f-H15.js.map +0 -1
  33. package/dist/chunks/dialog-DolPKFGS.js +0 -141
  34. package/dist/chunks/dialog-DolPKFGS.js.map +0 -1
  35. package/dist/chunks/settings-B0vhLoMi.js +0 -134
  36. package/dist/chunks/settings-B0vhLoMi.js.map +0 -1
  37. package/dist/sections/settings.js +0 -8
  38. package/dist/src/sections/settings/index.d.ts +0 -1
  39. package/dist/src/sections/settings/settings.d.ts +0 -170
@@ -1,6 +1,14 @@
1
1
  import { ComponentProps } from 'react';
2
- /** Size axis — padding, gap, and label size. */
2
+ /**
3
+ * Size axis — fixed heights on a 4px step (16 / 20 / 24) so the ladder stays
4
+ * proportional. Horizontal padding and type scale with each step; vertical
5
+ * centering comes from `items-center` on the base (no `py`).
6
+ */
3
7
  export declare const BADGE_SIZES: {
8
+ xs: {
9
+ classes: string;
10
+ description: string;
11
+ };
4
12
  sm: {
5
13
  classes: string;
6
14
  description: string;
@@ -10,40 +18,58 @@ export declare const BADGE_SIZES: {
10
18
  description: string;
11
19
  };
12
20
  };
13
- /** Color axis — a closed semantic set; each key tints text, fill, and ring. */
21
+ /**
22
+ * Color axis — a closed semantic set with one recipe per contrast. `high`
23
+ * fills with the state ink (text flips with it per mode); `low` tints text
24
+ * only and lets `LOW_MATERIAL` derive fill and ring from it.
25
+ */
14
26
  export declare const BADGE_COLORS: {
15
27
  gray: {
16
- classes: string;
28
+ high: string;
29
+ low: string;
30
+ description: string;
31
+ };
32
+ muted: {
33
+ high: string;
34
+ low: string;
17
35
  description: string;
18
36
  };
19
37
  brand: {
20
- classes: string;
38
+ high: string;
39
+ low: string;
21
40
  description: string;
22
41
  };
23
42
  success: {
24
- classes: string;
43
+ high: string;
44
+ low: string;
25
45
  description: string;
26
46
  };
27
47
  warning: {
28
- classes: string;
48
+ high: string;
49
+ low: string;
29
50
  description: string;
30
51
  };
31
52
  error: {
32
- classes: string;
53
+ high: string;
54
+ low: string;
33
55
  description: string;
34
56
  };
35
57
  };
36
58
  export type BadgeSize = keyof typeof BADGE_SIZES;
37
59
  export type BadgeColor = keyof typeof BADGE_COLORS;
60
+ export type BadgeContrast = "high" | "low";
38
61
  /** Compose the full class string for a badge — exported for reuse/testing. */
39
62
  export declare function badgeVariants(opts?: {
40
63
  color?: BadgeColor;
64
+ contrast?: BadgeContrast;
41
65
  size?: BadgeSize;
42
66
  className?: string;
43
67
  }): string;
44
68
  interface BadgeBaseProps {
45
69
  /** Semantic color. @default "gray" */
46
70
  color?: BadgeColor;
71
+ /** Solid state fill (`high`) or the soft currentColor tint (`low`). @default "high" */
72
+ contrast?: BadgeContrast;
47
73
  /** Size variant. @default "md" */
48
74
  size?: BadgeSize;
49
75
  }
@@ -56,7 +82,7 @@ export interface BadgeProps extends BadgeBaseProps, Omit<ComponentProps<"span">,
56
82
  * for a standalone action use a `Button`. Let `color` carry the meaning and keep
57
83
  * the label to a word or two.
58
84
  */
59
- export declare function Badge({ color, size, className, children, ...props }: BadgeProps): import("react").JSX.Element;
85
+ export declare function Badge({ color, contrast, size, className, children, ...props }: BadgeProps): import("react").JSX.Element;
60
86
  export declare namespace Badge {
61
87
  var displayName: string;
62
88
  }
@@ -1 +1 @@
1
- export { Badge, badgeVariants, BADGE_SIZES, BADGE_COLORS, type BadgeProps, type BadgeSize, type BadgeColor, } from './badge';
1
+ export { Badge, badgeVariants, BADGE_SIZES, BADGE_COLORS, type BadgeProps, type BadgeSize, type BadgeColor, type BadgeContrast, } from './badge';
@@ -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`
@@ -0,0 +1 @@
1
+ export { StatusView, STATUS_VIEW, STATUS_VIEW_COPY, STATUS_VIEW_SIZES, type StatusViewProps, type StatusViewSize, } from './status-view';
@@ -0,0 +1,74 @@
1
+ import { ComponentProps, ReactNode } from 'react';
2
+ /**
3
+ * Hugs its content (`w-fit`) and centers itself in the host (`mx-auto`); text
4
+ * stays start-aligned so the icon / title / description share one left edge.
5
+ * `max-w-lg` only caps long prose at Dialog width before it wraps.
6
+ */
7
+ export declare const STATUS_VIEW = "mx-auto flex w-fit max-w-lg min-w-0 flex-col items-start justify-center text-left";
8
+ /** Title + description group — flex column; gap comes from the size axis. */
9
+ export declare const STATUS_VIEW_COPY = "flex flex-col";
10
+ /**
11
+ * Size axis — retunes every slot. Title + description share Dialog's
12
+ * `font-display` ladder: `base` matches Dialog (`gap-1`); `sm` steps one rung
13
+ * down (`gap-0`) for cards and panels.
14
+ */
15
+ export declare const STATUS_VIEW_SIZES: {
16
+ sm: {
17
+ description: string;
18
+ classes: {
19
+ root: string;
20
+ icon: string;
21
+ copy: string;
22
+ title: string;
23
+ description: string;
24
+ actions: string;
25
+ };
26
+ };
27
+ base: {
28
+ description: string;
29
+ classes: {
30
+ root: string;
31
+ icon: string;
32
+ copy: string;
33
+ title: string;
34
+ description: string;
35
+ actions: string;
36
+ };
37
+ };
38
+ };
39
+ export type StatusViewSize = keyof typeof STATUS_VIEW_SIZES;
40
+ interface StatusViewBaseProps {
41
+ /** Decorative glyph above the title, usually a Tabler icon. Sized by the view (`*:size-*`), inked `text-pho-secondary`. */
42
+ icon?: ReactNode;
43
+ /** One short line naming the state ("No API keys yet", "Page not found"). */
44
+ title: ReactNode;
45
+ /** Supporting prose — what it means or what to do next. */
46
+ description?: ReactNode;
47
+ /** Optional action row (buttons / links). Empty states usually omit it. */
48
+ actions?: ReactNode;
49
+ /** Size variant. @default "base" */
50
+ size?: StatusViewSize;
51
+ }
52
+ export interface StatusViewProps extends StatusViewBaseProps, Omit<ComponentProps<"div">, keyof StatusViewBaseProps | "children"> {
53
+ }
54
+ /**
55
+ * StatusView — the standing-in view for a region with nothing to show or
56
+ * nothing that worked: icon over title over description, with an optional
57
+ * action row. The block shrink-wraps and centers itself while its text stays
58
+ * left-aligned; the host owns the footprint (a card body, or a `min-h-screen`
59
+ * flex-centered page). Use `size="sm"` inside cards, the default `base` for
60
+ * whole pages.
61
+ *
62
+ * Empty and error states share this one component — they differ only in what
63
+ * you pass:
64
+ *
65
+ * - **Empty** — usually no `icon` and no `actions`; the create affordance
66
+ * lives elsewhere on the page.
67
+ * - **Error** — pass a warning `icon`, `role="alert"` so it announces, and
68
+ * `actions` offering a way out (retry, reload, a route back).
69
+ */
70
+ export declare function StatusView({ icon, title, description, actions, size, className, ...props }: StatusViewProps): import("react").JSX.Element;
71
+ export declare namespace StatusView {
72
+ var displayName: string;
73
+ }
74
+ export {};
@@ -11,5 +11,6 @@ export * from './components/input';
11
11
  export * from './components/input-group';
12
12
  export * from './components/card';
13
13
  export * from './components/dialog';
14
+ export * from './components/status-view';
14
15
  export * from './sections/sidebar';
15
- export * from './sections/settings';
16
+ export * from './sections/basic-page';
@@ -0,0 +1,332 @@
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
+ * One card row that can host subviews. Same padding rhythm as a plain row —
35
+ * an optional trailing `ItemPanel` breaks out of it on its own.
36
+ */
37
+ export declare const BASIC_PAGE_ITEM = "flex flex-col gap-3 px-5 py-4";
38
+ /**
39
+ * Secondary strip closing an `Item` that is last in its card. Negative
40
+ * margins escape the item's `px-5 pb-4` so the gray sheet runs edge-to-edge
41
+ * and owns the row's bottom padding. The body above stays square and the
42
+ * sheet's top is flush against it; only the bottom rounds, nesting into the
43
+ * card's 12px corner (11px inside the 1px border). Content lands back on the
44
+ * item's left gutter.
45
+ */
46
+ export declare const BASIC_PAGE_ITEM_PANEL = "-mx-5 -mb-4 flex items-center rounded-b-[calc(0.75rem-1px)] bg-pho-secondary py-1 pr-3 pl-5";
47
+ /**
48
+ * Mid-list variant — the full-bleed sheet only reads well against the card's
49
+ * bottom corner, so when later rows follow, the panel becomes an inset chip
50
+ * instead: fully rounded at the small-control radius, overhanging the text
51
+ * gutter by 8px per side (content stays aligned with the body above), and
52
+ * leaving the row's own bottom padding and the card's hairline divider alone.
53
+ * `-mt-1.5` collapses the item's `gap-3` (12px) to an effective `gap-1.5` (6px).
54
+ */
55
+ export declare const BASIC_PAGE_ITEM_PANEL_DIVIDED = "-mx-2 -mt-1.5 flex items-center rounded-sm bg-pho-secondary px-2 py-0.5";
56
+ /**
57
+ * Page chrome — centered column at the standard content width. Hosts
58
+ * `Title` + `Stack` (and any route-level siblings). Prefer this over
59
+ * ad-hoc `max-w-*` wrappers in the app shell.
60
+ *
61
+ * `44.5rem` = the 42rem (`2xl`) content column plus the two `px-5` gutters,
62
+ * so wide viewports render exactly as before while narrow ones keep a 20px
63
+ * breathing edge instead of running content into the viewport.
64
+ *
65
+ * `relative` anchors `Back`, which floats in the top padding — the reserved
66
+ * zone that keeps `Title` at the same position on every page.
67
+ */
68
+ declare function Root({ className, ...props }: ComponentProps<"div"> & {
69
+ ref?: Ref<HTMLDivElement>;
70
+ }): import("react").JSX.Element;
71
+ /**
72
+ * Page title — largest heading on the surface. Plain-text children get a slug
73
+ * `id` for hash targets (`#profile`); the heading itself is not a link.
74
+ */
75
+ declare function Title({ id, className, children, ...props }: ComponentProps<"h1"> & {
76
+ ref?: Ref<HTMLHeadingElement>;
77
+ }): import("react").JSX.Element;
78
+ /**
79
+ * Vertical stack under the page title. Blocks (forms / sections) use `gap-10`;
80
+ * each block lays out its children with `gap-3`.
81
+ */
82
+ declare function Stack({ className, ...props }: ComponentProps<"div"> & {
83
+ ref?: Ref<HTMLDivElement>;
84
+ }): import("react").JSX.Element;
85
+ /**
86
+ * Title + optional description pair. Keeps that couple tight (`gap-0.5`) so
87
+ * the block's `gap-3` only applies between this header and the card.
88
+ */
89
+ declare function Header({ className, ...props }: ComponentProps<"div"> & {
90
+ ref?: Ref<HTMLDivElement>;
91
+ }): import("react").JSX.Element;
92
+ /**
93
+ * Section heading — same padding as the page title, smaller type. Hashable via
94
+ * slug `id` (not a link).
95
+ */
96
+ declare function SectionTitle({ id, className, children, ...props }: ComponentProps<"h2"> & {
97
+ ref?: Ref<HTMLHeadingElement>;
98
+ }): import("react").JSX.Element;
99
+ /**
100
+ * Supporting copy under a section title. Same display step as the title;
101
+ * color + normal weight keep it secondary. Always wrap with `Header`.
102
+ */
103
+ declare function Description({ className, ...props }: ComponentProps<"p"> & {
104
+ ref?: Ref<HTMLParagraphElement>;
105
+ }): import("react").JSX.Element;
106
+ /**
107
+ * Optional caption below a section's `Card` (outside the card). One step
108
+ * smaller than `Description` (`text-xs`). Not the card-internal dirty-state
109
+ * `Footer`.
110
+ */
111
+ declare function SectionFooter({ className, ...props }: ComponentProps<"p"> & {
112
+ ref?: Ref<HTMLParagraphElement>;
113
+ }): import("react").JSX.Element;
114
+ /** Bordered card that stacks rows with hairline dividers. */
115
+ declare function Card({ className, ...props }: ComponentProps<"div"> & {
116
+ ref?: Ref<HTMLDivElement>;
117
+ }): import("react").JSX.Element;
118
+ /**
119
+ * One card row that can host subviews. Padding matches a plain row, so use it
120
+ * for any row that may grow an `ItemPanel` — primary content goes straight in
121
+ * as children; the panel, when present, is the last child.
122
+ *
123
+ * ```tsx
124
+ * <BasicPage.Card>
125
+ * <BasicPage.Item>
126
+ * …name, meta, actions…
127
+ * <BasicPage.ItemPanel divided>…secret / expanded content…</BasicPage.ItemPanel>
128
+ * </BasicPage.Item>
129
+ * …later siblings (pass `divided` on the panel above)…
130
+ * </BasicPage.Card>
131
+ * ```
132
+ */
133
+ declare function Item({ className, ...props }: ComponentProps<"div"> & {
134
+ ref?: Ref<HTMLDivElement>;
135
+ }): import("react").JSX.Element;
136
+ export interface BasicPageItemPanelProps extends ComponentProps<"div"> {
137
+ /**
138
+ * Set when later card rows follow this item. The panel then renders as an
139
+ * inset rounded chip (keeping the card's own divider) instead of the
140
+ * full-bleed bottom sheet, which only reads well against the card's corner.
141
+ * @default false
142
+ */
143
+ divided?: boolean;
144
+ ref?: Ref<HTMLDivElement>;
145
+ }
146
+ /**
147
+ * Secondary strip at the end of an `Item`. When the item closes the card,
148
+ * it's a gray sheet flush under the body, full-bleed and owning the row's
149
+ * bottom padding, rounding only into the card's bottom corner. Pass `divided`
150
+ * when later card rows follow — the panel becomes an inset rounded chip and
151
+ * the card's hairline divider stays in charge. Content is already a centered
152
+ * flex row either way.
153
+ */
154
+ declare function ItemPanel({ divided, className, ...props }: BasicPageItemPanelProps): import("react").JSX.Element;
155
+ /**
156
+ * Standalone callout. Pair with `CalloutTitle` / `CalloutDescription` (and
157
+ * optional actions) for a featured block — e.g. an invite form. Do not wrap
158
+ * in `Card`; this is already the outer surface.
159
+ */
160
+ declare function Callout({ className, ...props }: ComponentProps<"div"> & {
161
+ ref?: Ref<HTMLDivElement>;
162
+ }): import("react").JSX.Element;
163
+ /** Callout heading — same type as `SectionTitle`. Hashable via slug `id`. */
164
+ declare function CalloutTitle({ id, className, children, ...props }: ComponentProps<"h2"> & {
165
+ ref?: Ref<HTMLHeadingElement>;
166
+ }): import("react").JSX.Element;
167
+ /** Supporting copy under a callout title. */
168
+ declare function CalloutDescription({ className, ...props }: ComponentProps<"p"> & {
169
+ ref?: Ref<HTMLParagraphElement>;
170
+ }): import("react").JSX.Element;
171
+ export interface BasicPageRowProps extends Omit<ComponentProps<"div">, "title"> {
172
+ /** Left-side field name. */
173
+ label: string;
174
+ /** Optional helper under the label. */
175
+ description?: ReactNode;
176
+ /** Associates the label with a control id. When omitted, the label is not a `<label>`. */
177
+ htmlFor?: string;
178
+ ref?: Ref<HTMLDivElement>;
179
+ }
180
+ /**
181
+ * One row: label (+ optional description) left, control right.
182
+ * Pass `htmlFor` when the row wraps a labelled input; omit it for display/
183
+ * action rows (avatar, danger-zone delete).
184
+ */
185
+ declare function Row({ label, description, htmlFor, className, children, ...props }: BasicPageRowProps): import("react").JSX.Element;
186
+ /** Read-only value on the right side of a row (email, etc.). */
187
+ declare function Value({ className, ...props }: ComponentProps<"p"> & {
188
+ ref?: Ref<HTMLParagraphElement>;
189
+ }): import("react").JSX.Element;
190
+ /**
191
+ * Inline error under a row (still inside the card). Matches card horizontal
192
+ * padding so hosts don't re-apply `px-5`.
193
+ */
194
+ declare function Alert({ className, ...props }: ComponentProps<"p"> & {
195
+ ref?: Ref<HTMLParagraphElement>;
196
+ }): import("react").JSX.Element;
197
+ export interface BasicPageFooterProps extends ComponentProps<"div"> {
198
+ /**
199
+ * Animated presence for dirty-state bars: the card glides open/closed on a
200
+ * height spring while the content fades. Omit for a static, always-visible
201
+ * footer (no animation). No reveal plays on first mount — only on flips.
202
+ */
203
+ show?: boolean;
204
+ ref?: Ref<HTMLDivElement>;
205
+ }
206
+ /**
207
+ * Trailing bar inside a card — typically a save status + submit button that
208
+ * appears after the form is dirty. Same horizontal/vertical padding as a row.
209
+ * Pass `show` (instead of conditional rendering) to animate the bar in and
210
+ * out following the onboarding card's motion language.
211
+ */
212
+ declare function Footer({ show, className, ...props }: BasicPageFooterProps): import("react").JSX.Element;
213
+ export interface BasicPageBackProps extends Omit<ButtonLinkProps, "variant" | "size" | "shape" | "prefix" | "suffix" | "svgOnly" | "href"> {
214
+ /** Parent destination — rendered through the host's `LinkProvider`. */
215
+ href: string;
216
+ /** Label — name the parent page. @default "Back" */
217
+ children?: ReactNode;
218
+ }
219
+ /**
220
+ * Back link for a subpage — a small ghost `ButtonLink`; place inside `Root`,
221
+ * before `Title`. It floats in `Root`'s reserved top padding (vertically
222
+ * centered in the 48px zone) instead of taking layout space, so `Title` sits
223
+ * at exactly the same position whether or not a page has one.
224
+ *
225
+ * The offsets align the *label* glyph with the title glyph. Title text sits
226
+ * 40px in (`px-5` root gutter + `px-5` title pad); inside a `sm` ghost button
227
+ * the label sits 29.75px in (8px icon-side pad + 15.75px chevron, 1.125em at
228
+ * the 14px `text-base`, + 4px gap + 2px label-span `px-0.5`) — so `left` is
229
+ * the 10.25px difference. `top-2` centers the 32px button in the 48px
230
+ * reserved zone. The chevron and the ghost hover pill hang in the padding —
231
+ * the usual optical alignment for borderless controls.
232
+ */
233
+ declare function Back({ href, className, children, ...props }: BasicPageBackProps): import("react").JSX.Element;
234
+ export interface BasicPageRowLinkProps extends Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href" | "title"> {
235
+ /** Left-side field name — plain text, or compose with a trailing badge. */
236
+ label: ReactNode;
237
+ /** Optional helper under the label. */
238
+ description?: ReactNode;
239
+ /** Optional media left of the label (e.g. project avatar). */
240
+ leading?: ReactNode;
241
+ /** Subpage destination — rendered through the host's `LinkProvider`. */
242
+ href: string;
243
+ /** Optional trailing value before the chevron (compose with `Value`). */
244
+ children?: ReactNode;
245
+ ref?: Ref<HTMLAnchorElement>;
246
+ }
247
+ /**
248
+ * A row that navigates — the whole row is one link into a subpage, with a
249
+ * trailing chevron cue. Same geometry as `Row`; hover lifts the row, and
250
+ * first/last rows follow the card's corner radius.
251
+ */
252
+ declare function RowLink({ label, description, leading, href, className, children, ...props }: BasicPageRowLinkProps): import("react").JSX.Element;
253
+ /**
254
+ * Basic page layout. One stack, one gap rhythm — only title scale differs:
255
+ *
256
+ * ```tsx
257
+ * <BasicPage.Root>
258
+ * <BasicPage.Title>Profile</BasicPage.Title>
259
+ * <BasicPage.Stack>
260
+ * <form>
261
+ * <BasicPage.Card>
262
+ * <BasicPage.Row label="Name" htmlFor="first-name">
263
+ * <Input id="first-name" size="sm" />
264
+ * </BasicPage.Row>
265
+ * </BasicPage.Card>
266
+ * </form>
267
+ * <form>
268
+ * <BasicPage.Header>
269
+ * <BasicPage.SectionTitle>Agent profile</BasicPage.SectionTitle>
270
+ * <BasicPage.Description>…</BasicPage.Description>
271
+ * </BasicPage.Header>
272
+ * <BasicPage.Card>…</BasicPage.Card>
273
+ * </form>
274
+ * <div>
275
+ * <BasicPage.SectionTitle>Danger zone</BasicPage.SectionTitle>
276
+ * <BasicPage.Card>
277
+ * <BasicPage.Row label="Delete account" description="…">
278
+ * <Button color="destructive" size="sm">Delete</Button>
279
+ * </BasicPage.Row>
280
+ * </BasicPage.Card>
281
+ * </div>
282
+ * <BasicPage.Callout>
283
+ * <BasicPage.Header>
284
+ * <BasicPage.CalloutTitle>Invitations</BasicPage.CalloutTitle>
285
+ * <BasicPage.CalloutDescription>…</BasicPage.CalloutDescription>
286
+ * </BasicPage.Header>
287
+ * …invite form…
288
+ * </BasicPage.Callout>
289
+ * </BasicPage.Stack>
290
+ * </BasicPage.Root>
291
+ * ```
292
+ *
293
+ * Subpage — `Back` above the title, `RowLink` rows to drill in:
294
+ *
295
+ * ```tsx
296
+ * <BasicPage.Root>
297
+ * <BasicPage.Back href="/settings">Settings</BasicPage.Back>
298
+ * <BasicPage.Title>Billing</BasicPage.Title>
299
+ * <BasicPage.Stack>
300
+ * <div>
301
+ * <BasicPage.Card>
302
+ * <BasicPage.RowLink label="Invoices" href="/settings/invoices">
303
+ * <BasicPage.Value>12 documents</BasicPage.Value>
304
+ * </BasicPage.RowLink>
305
+ * </BasicPage.Card>
306
+ * </div>
307
+ * </BasicPage.Stack>
308
+ * </BasicPage.Root>
309
+ * ```
310
+ */
311
+ export declare const BasicPage: {
312
+ Root: typeof Root;
313
+ Back: typeof Back;
314
+ Title: typeof Title;
315
+ Stack: typeof Stack;
316
+ Header: typeof Header;
317
+ SectionTitle: typeof SectionTitle;
318
+ Description: typeof Description;
319
+ SectionFooter: typeof SectionFooter;
320
+ Card: typeof Card;
321
+ Item: typeof Item;
322
+ ItemPanel: typeof ItemPanel;
323
+ Callout: typeof Callout;
324
+ CalloutTitle: typeof CalloutTitle;
325
+ CalloutDescription: typeof CalloutDescription;
326
+ Row: typeof Row;
327
+ RowLink: typeof RowLink;
328
+ Value: typeof Value;
329
+ Alert: typeof Alert;
330
+ Footer: typeof Footer;
331
+ };
332
+ export {};
@@ -0,0 +1 @@
1
+ export { BasicPage, BASIC_PAGE_CARD, BASIC_PAGE_CALLOUT, BASIC_PAGE_ROW, BASIC_PAGE_ITEM, BASIC_PAGE_ITEM_PANEL, BASIC_PAGE_ITEM_PANEL_DIVIDED, type BasicPageRowProps, type BasicPageRowLinkProps, type BasicPageBackProps, type BasicPageFooterProps, type BasicPageItemPanelProps, } 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@photon-ai/pho-ui",
3
- "version": "1.2.1",
3
+ "version": "1.4.0",
4
4
  "description": "Pho Design System — Photon's React component library, built on Base UI",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -201,6 +201,10 @@
201
201
  "types": "./dist/src/components/spinner/index.d.ts",
202
202
  "import": "./dist/components/spinner.js"
203
203
  },
204
+ "./components/status-view": {
205
+ "types": "./dist/src/components/status-view/index.d.ts",
206
+ "import": "./dist/components/status-view.js"
207
+ },
204
208
  "./components/notification": {
205
209
  "types": "./dist/src/components/notification/index.d.ts",
206
210
  "import": "./dist/components/notification.js"
@@ -209,9 +213,9 @@
209
213
  "types": "./dist/src/sections/sidebar/index.d.ts",
210
214
  "import": "./dist/sections/sidebar.js"
211
215
  },
212
- "./sections/settings": {
213
- "types": "./dist/src/sections/settings/index.d.ts",
214
- "import": "./dist/sections/settings.js"
216
+ "./sections/basic-page": {
217
+ "types": "./dist/src/sections/basic-page/index.d.ts",
218
+ "import": "./dist/sections/basic-page.js"
215
219
  },
216
220
  "./icons": {
217
221
  "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);