@realiizlabs/admin 0.7.2 → 0.8.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.
@@ -0,0 +1,147 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, ButtonHTMLAttributes, AnchorHTMLAttributes } from 'react';
3
+
4
+ /**
5
+ * Light / dark, remembered per browser. The attribute lives on the .rz-admin
6
+ * root (not <html>) so the host site's own theme is never touched.
7
+ *
8
+ * No flash of the wrong theme: the server renders defaultTheme, and the effect
9
+ * only flips when a stored value differs — a single client-side change, never
10
+ * a mismatch between server HTML and first client render.
11
+ */
12
+ type Theme = "light" | "dark";
13
+ declare function useTheme(defaultTheme?: Theme): [Theme, (t: Theme) => void];
14
+ declare function ThemeToggle({ theme, onChange }: {
15
+ theme: Theme;
16
+ onChange: (t: Theme) => void;
17
+ }): react.JSX.Element;
18
+
19
+ interface ShellUser {
20
+ email: string;
21
+ name?: string | null;
22
+ }
23
+ declare function firstNameOf(user: ShellUser): string;
24
+ declare function initialsOf(user: ShellUser): string;
25
+ declare function AccountMenu({ user, role, theme, onTheme, signOutPath }: {
26
+ user: ShellUser;
27
+ role: string;
28
+ theme: Theme;
29
+ onTheme: (t: Theme) => void;
30
+ signOutPath: string;
31
+ }): react.JSX.Element;
32
+
33
+ /**
34
+ * Sidebar — items from the content-type registry (via navFromContentTypes),
35
+ * active by href prefix, collapsible with the state remembered per browser.
36
+ * Plain anchors: the package does not know which router the host uses.
37
+ */
38
+ interface NavItem {
39
+ id: string;
40
+ label: string;
41
+ href: string;
42
+ /** Icon id; unknown ids fall back to a document icon. Defaults to `id`. */
43
+ icon?: string;
44
+ }
45
+ declare function isActive(item: NavItem, activeHref: string, basePath: string): boolean;
46
+ declare function Sidebar({ items, activeHref, basePath }: {
47
+ items: NavItem[];
48
+ activeHref: string;
49
+ basePath: string;
50
+ }): react.JSX.Element;
51
+
52
+ interface AdminShellProps {
53
+ siteName: string;
54
+ logoUrl?: string | null;
55
+ /** null when nobody is signed in (sign-in page, not-configured page). */
56
+ user: ShellUser | null;
57
+ role: string | null;
58
+ nav: NavItem[];
59
+ /** The current pathname, for the active nav item. */
60
+ activeHref: string;
61
+ /** Default "/admin". */
62
+ basePath?: string;
63
+ /** Default `${basePath}/auth/signout`. */
64
+ signOutPath?: string;
65
+ defaultTheme?: Theme;
66
+ children: ReactNode;
67
+ }
68
+ declare function AdminShell({ siteName, logoUrl, user, role, nav, activeHref, basePath, signOutPath, defaultTheme, children, }: AdminShellProps): react.JSX.Element;
69
+
70
+ declare function TopBar({ siteName, logoUrl, homeHref, user, role, theme, onTheme, signOutPath }: {
71
+ siteName: string;
72
+ logoUrl?: string | null;
73
+ homeHref: string;
74
+ user: ShellUser | null;
75
+ role: string | null;
76
+ theme: Theme;
77
+ onTheme: (t: Theme) => void;
78
+ signOutPath: string;
79
+ }): react.JSX.Element;
80
+
81
+ type ButtonVariant = "primary" | "secondary" | "danger" | "ghost";
82
+ type ButtonSize = "sm" | "md";
83
+ declare function buttonClass(variant?: ButtonVariant, size?: ButtonSize, extra?: string): string;
84
+ /** Kept for parity with the tracker's API; returns the class string to spread onto an <a>. */
85
+ declare function buttonStyle(variant?: ButtonVariant, size?: ButtonSize): {
86
+ className: string;
87
+ };
88
+ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
89
+ variant?: ButtonVariant;
90
+ size?: ButtonSize;
91
+ }
92
+ declare function Button({ variant, size, className, type, ...rest }: ButtonProps): react.JSX.Element;
93
+ interface LinkButtonProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
94
+ variant?: ButtonVariant;
95
+ size?: ButtonSize;
96
+ }
97
+ declare function LinkButton({ variant, size, className, ...rest }: LinkButtonProps): react.JSX.Element;
98
+
99
+ declare function Card({ title, meta, children, actions, className }: {
100
+ title?: ReactNode;
101
+ meta?: ReactNode;
102
+ children?: ReactNode;
103
+ actions?: ReactNode;
104
+ className?: string;
105
+ }): react.JSX.Element;
106
+ declare function PageHeader({ eyebrow, title, subtitle, actions }: {
107
+ eyebrow?: ReactNode;
108
+ title: ReactNode;
109
+ subtitle?: ReactNode;
110
+ actions?: ReactNode;
111
+ }): react.JSX.Element;
112
+ type PillTone = "up" | "warn" | "down" | "neutral" | "plain";
113
+ declare function StatusPill({ tone, children, title }: {
114
+ tone?: PillTone;
115
+ children: ReactNode;
116
+ title?: string;
117
+ }): react.JSX.Element;
118
+ declare function EmptyState({ title, children, action }: {
119
+ title: ReactNode;
120
+ children?: ReactNode;
121
+ action?: ReactNode;
122
+ }): react.JSX.Element;
123
+
124
+ /** Seven stroke icons, 18px, currentColor. Registry ids pick one; unknown ids get `doc`. */
125
+ type IconName = "home" | "posts" | "events" | "venues" | "users" | "settings" | "doc";
126
+ declare function Icon({ name }: {
127
+ name: IconName;
128
+ }): react.JSX.Element;
129
+ declare function iconFor(id: string): IconName;
130
+
131
+ /**
132
+ * SHELL_CSS — the design system, as one string the shell injects.
133
+ *
134
+ * Palette borrowed from the visibility tracker (Realiiz's back-office look):
135
+ * warm bone/ink with an operative-green signal. Light is the default so every
136
+ * install looks finished; dark is one attribute away. Everything is scoped to
137
+ * `.rz-admin` so a host site's variables never leak in and ours never leak out.
138
+ *
139
+ * A site overrides by redeclaring any variable on `.rz-admin` — e.g.
140
+ * .rz-admin { --signal: #0a58ca; --rz-font: var(--font-inter), sans-serif; }
141
+ *
142
+ * The block at the end maps the forms-ui variables onto these tokens, so
143
+ * ContentForm and MagicLinkForm re-skin without touching their own code.
144
+ */
145
+ declare const SHELL_CSS = "\n.rz-admin, .rz-admin[data-theme=\"light\"] {\n --ink: #1a1916; --bone: #f5f0e8; --signal: #166534;\n --surface-0: #faf7f2; --surface-1: #f5f0e8; --surface-2: #ede8de; --surface-3: #e2dbd0;\n --text-primary: #1a1916; --text-secondary: #4a4640; --text-muted: #8a8480;\n --signal-dim: #15803d; --signal-glow: #14532d; --signal-warn: #d97706; --series-blue: #2563eb;\n --border: #e2dbd0; --separator: #ede8de;\n --status-up: var(--signal); --status-down: #dc2626; --status-neutral: var(--text-muted); --status-blocked: var(--border);\n --signal-fg: #ffffff;\n}\n.rz-admin[data-theme=\"dark\"] {\n --ink: #e8e4d9; --bone: #1a1a18; --signal: #4ade80;\n --surface-0: #111110; --surface-1: #1a1a18; --surface-2: #252522; --surface-3: #2e2e2a;\n --text-primary: #e8e4d9; --text-secondary: #a8a49a; --text-muted: #6b6762;\n --signal-dim: #22c55e; --signal-glow: #86efac; --signal-warn: #f59e0b; --series-blue: #3b82f6;\n --border: #2e2e2a; --separator: #252522;\n --status-up: var(--signal); --status-down: #f87171; --status-neutral: var(--text-muted); --status-blocked: var(--border);\n --signal-fg: #111110;\n}\n.rz-admin {\n --rz-font: system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n --rz-font-display: var(--rz-font);\n --rz-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;\n --rz-radius: 6px;\n --rz-sidebar: 220px; --rz-sidebar-collapsed: 58px; --rz-topbar: 52px;\n\n /* forms-ui speaks these; map them once so both packages agree */\n --realiiz-form-fg: var(--text-primary); --realiiz-form-muted: var(--text-muted);\n --realiiz-form-border: var(--border); --realiiz-form-accent: var(--signal);\n --realiiz-form-accent-fg: var(--signal-fg); --realiiz-form-danger: var(--status-down);\n --realiiz-form-radius: var(--rz-radius); --realiiz-form-font: var(--rz-font);\n --realiiz-form-focus-ring: 0 0 0 3px color-mix(in srgb, var(--signal) 25%, transparent);\n\n box-sizing: border-box; min-height: 100vh; display: flex; flex-direction: column;\n background: var(--surface-0); color: var(--text-primary);\n font-family: var(--rz-font); font-size: 14px; line-height: 1.45;\n}\n.rz-admin *, .rz-admin *::before, .rz-admin *::after { box-sizing: inherit; }\n.rz-admin a { color: inherit; }\n.rz-admin h1, .rz-admin h2, .rz-admin h3 { font-family: var(--rz-font-display); color: var(--text-primary); margin: 0; }\n.rz-admin .realiiz-form__control { background: var(--surface-0); }\n\n/* ---- frame ---------------------------------------------------------- */\n.rz-topbar { height: var(--rz-topbar); display: flex; align-items: center; gap: 1rem; padding: 0 1.25rem; border-bottom: 1px solid var(--border); background: var(--surface-1); flex-shrink: 0; }\n.rz-topbar__brand { display: flex; align-items: center; gap: .6rem; font-weight: 600; font-size: .9375rem; text-decoration: none; }\n.rz-topbar__logo { height: 22px; width: auto; display: block; }\n.rz-topbar__divider { width: 1px; height: 18px; background: var(--border); }\n.rz-topbar__right { margin-left: auto; display: flex; align-items: center; gap: 1rem; }\n.rz-topbar__greeting { color: var(--text-secondary); font-size: .8rem; }\n.rz-body { display: flex; flex: 1; min-height: 0; }\n.rz-sidebar { width: var(--rz-sidebar); flex-shrink: 0; border-right: 1px solid var(--border); background: var(--surface-1); display: flex; flex-direction: column; transition: width .15s ease; }\n.rz-sidebar--collapsed { width: var(--rz-sidebar-collapsed); }\n.rz-sidebar__nav { padding: .75rem 0; flex: 1; overflow-y: auto; }\n.rz-sidebar__item { display: flex; align-items: center; gap: .65rem; padding: .5rem .9rem; margin: 1px .5rem; border-radius: var(--rz-radius); color: var(--text-secondary); text-decoration: none; font-size: .8125rem; border-left: 2px solid transparent; white-space: nowrap; overflow: hidden; }\n.rz-sidebar__item:hover { background: var(--surface-2); color: var(--text-primary); }\n.rz-sidebar__item--active { background: var(--surface-2); color: var(--text-primary); border-left-color: var(--signal); }\n.rz-sidebar--collapsed .rz-sidebar__label { display: none; }\n.rz-sidebar__collapse { border-top: 1px solid var(--separator); padding: .3rem .5rem; }\n.rz-sidebar__collapse button { width: 100%; }\n.rz-main { flex: 1; min-width: 0; overflow-x: hidden; }\n.rz-content { max-width: 960px; margin: 0 auto; padding: 28px 28px 80px; }\n\n/* ---- primitives ----------------------------------------------------- */\n.rz-btn { font-family: inherit; font-weight: 500; border-radius: var(--rz-radius); cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; justify-content: center; gap: .4rem; white-space: nowrap; line-height: 1.2; }\n.rz-btn--md { padding: .45rem 1rem; font-size: .8125rem; }\n.rz-btn--sm { padding: .3rem .7rem; font-size: .75rem; }\n.rz-btn--primary { background: var(--signal); color: var(--signal-fg); border: 1px solid transparent; }\n.rz-btn--secondary { background: var(--surface-3); color: var(--text-secondary); border: 1px solid var(--border); }\n.rz-btn--danger { background: transparent; color: var(--status-down); border: 1px solid var(--status-down); }\n.rz-btn--ghost { background: transparent; color: var(--text-secondary); border: 1px solid var(--border); }\n.rz-btn:disabled { opacity: .55; cursor: default; }\n.rz-card { background: var(--surface-1); border: 1px solid var(--border); border-radius: 8px; padding: 18px 20px; }\n.rz-card__title { font-size: .9375rem; font-weight: 600; margin-bottom: .35rem; }\n.rz-card__meta { color: var(--text-muted); font-size: .8rem; }\n.rz-card__actions { display: flex; gap: .5rem; margin-top: .9rem; flex-wrap: wrap; }\n.rz-grid { display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }\n.rz-pagehead { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }\n.rz-pagehead__eyebrow { font-family: var(--rz-font-mono); font-size: .6875rem; text-transform: uppercase; letter-spacing: .08em; color: var(--text-muted); margin-bottom: .3rem; }\n.rz-pagehead h1 { font-size: 1.375rem; font-weight: 600; }\n.rz-pagehead__sub { color: var(--text-secondary); font-size: .85rem; margin-top: .25rem; }\n.rz-pill { display: inline-flex; align-items: center; gap: .4rem; font-family: var(--rz-font-mono); font-size: .6875rem; text-transform: uppercase; letter-spacing: .06em; padding: .15rem .5rem; border: 1px solid var(--border); border-radius: 999px; color: var(--text-secondary); }\n.rz-pill::before { content: \"\"; width: 7px; height: 7px; border-radius: 50%; background: var(--status-neutral); }\n.rz-pill--up::before { background: var(--status-up); } .rz-pill--warn::before { background: var(--signal-warn); } .rz-pill--down::before { background: var(--status-down); }\n.rz-pill--plain::before { display: none; }\n.rz-empty { border: 1px dashed var(--border); border-radius: 8px; padding: 32px; text-align: center; color: var(--text-secondary); }\n.rz-empty h3 { font-size: 1rem; margin-bottom: .4rem; }\n.rz-table { width: 100%; border-collapse: collapse; background: var(--surface-1); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }\n.rz-table th, .rz-table td { text-align: left; padding: 10px 14px; border-bottom: 1px solid var(--separator); font-size: .875rem; vertical-align: top; }\n.rz-table th { font-family: var(--rz-font-mono); font-size: .6875rem; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); background: var(--surface-2); }\n.rz-table tr:last-child td { border-bottom: 0; }\n.rz-muted { color: var(--text-muted); }\n.rz-alert { border: 1px solid var(--status-down); color: var(--status-down); border-radius: var(--rz-radius); padding: 12px 14px; margin: 0 0 16px; }\n.rz-ok { border: 1px solid var(--status-up); color: var(--signal-dim); border-radius: var(--rz-radius); padding: 12px 14px; margin: 0 0 16px; }\n\n/* ---- account menu --------------------------------------------------- */\n.rz-avatar { width: 2rem; height: 2rem; border-radius: 50%; background: var(--surface-3); border: 1px solid var(--border); color: var(--text-primary); font-size: .6875rem; font-weight: 600; letter-spacing: .04em; display: flex; align-items: center; justify-content: center; cursor: pointer; font-family: inherit; padding: 0; }\n.rz-menu { position: absolute; right: 0; top: calc(100% + 8px); min-width: 240px; background: var(--surface-1); border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 10px 30px rgba(0,0,0,.12); padding: 6px; z-index: 50; }\n.rz-menu__who { padding: 10px 10px 8px; }\n.rz-menu__name { font-weight: 600; font-size: .875rem; }\n.rz-menu__email { color: var(--text-muted); font-size: .75rem; word-break: break-all; }\n.rz-menu__sep { height: 1px; background: var(--separator); margin: 4px 0; }\n.rz-menu__row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 10px; font-size: .8125rem; color: var(--text-secondary); }\n.rz-menu__signout { padding: 6px; }\n.rz-menu__signout .rz-btn { width: 100%; }\n.rz-seg { display: inline-flex; border: 1px solid var(--border); border-radius: var(--rz-radius); overflow: hidden; }\n.rz-seg button { font: inherit; font-size: .75rem; padding: .25rem .6rem; background: transparent; color: var(--text-secondary); border: 0; cursor: pointer; }\n.rz-seg button[aria-pressed=\"true\"] { background: var(--surface-3); color: var(--text-primary); }\n\n/* ---- mobile: sidebar becomes a strip under the top bar --------------- */\n@media (max-width: 720px) {\n .rz-body { flex-direction: column; }\n .rz-sidebar, .rz-sidebar--collapsed { width: 100%; border-right: 0; border-bottom: 1px solid var(--border); flex-direction: row; }\n .rz-sidebar__nav { display: flex; padding: .35rem .25rem; overflow-x: auto; }\n .rz-sidebar__item { flex-direction: column; gap: .2rem; font-size: .6875rem; padding: .4rem .6rem; border-left: 0; border-bottom: 2px solid transparent; }\n .rz-sidebar__item--active { border-bottom-color: var(--signal); }\n .rz-sidebar--collapsed .rz-sidebar__label { display: block; }\n .rz-sidebar__collapse { display: none; }\n .rz-topbar__greeting { display: none; }\n .rz-content { padding: 18px 14px 60px; }\n}\n";
146
+
147
+ export { AccountMenu, AdminShell, type AdminShellProps, Button, type ButtonSize, type ButtonVariant, Card, EmptyState, Icon, type IconName, LinkButton, type NavItem, PageHeader, type PillTone, SHELL_CSS, type ShellUser, Sidebar, StatusPill, type Theme, ThemeToggle, TopBar, buttonClass, buttonStyle, firstNameOf, iconFor, initialsOf, isActive, useTheme };
@@ -0,0 +1,147 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, ButtonHTMLAttributes, AnchorHTMLAttributes } from 'react';
3
+
4
+ /**
5
+ * Light / dark, remembered per browser. The attribute lives on the .rz-admin
6
+ * root (not <html>) so the host site's own theme is never touched.
7
+ *
8
+ * No flash of the wrong theme: the server renders defaultTheme, and the effect
9
+ * only flips when a stored value differs — a single client-side change, never
10
+ * a mismatch between server HTML and first client render.
11
+ */
12
+ type Theme = "light" | "dark";
13
+ declare function useTheme(defaultTheme?: Theme): [Theme, (t: Theme) => void];
14
+ declare function ThemeToggle({ theme, onChange }: {
15
+ theme: Theme;
16
+ onChange: (t: Theme) => void;
17
+ }): react.JSX.Element;
18
+
19
+ interface ShellUser {
20
+ email: string;
21
+ name?: string | null;
22
+ }
23
+ declare function firstNameOf(user: ShellUser): string;
24
+ declare function initialsOf(user: ShellUser): string;
25
+ declare function AccountMenu({ user, role, theme, onTheme, signOutPath }: {
26
+ user: ShellUser;
27
+ role: string;
28
+ theme: Theme;
29
+ onTheme: (t: Theme) => void;
30
+ signOutPath: string;
31
+ }): react.JSX.Element;
32
+
33
+ /**
34
+ * Sidebar — items from the content-type registry (via navFromContentTypes),
35
+ * active by href prefix, collapsible with the state remembered per browser.
36
+ * Plain anchors: the package does not know which router the host uses.
37
+ */
38
+ interface NavItem {
39
+ id: string;
40
+ label: string;
41
+ href: string;
42
+ /** Icon id; unknown ids fall back to a document icon. Defaults to `id`. */
43
+ icon?: string;
44
+ }
45
+ declare function isActive(item: NavItem, activeHref: string, basePath: string): boolean;
46
+ declare function Sidebar({ items, activeHref, basePath }: {
47
+ items: NavItem[];
48
+ activeHref: string;
49
+ basePath: string;
50
+ }): react.JSX.Element;
51
+
52
+ interface AdminShellProps {
53
+ siteName: string;
54
+ logoUrl?: string | null;
55
+ /** null when nobody is signed in (sign-in page, not-configured page). */
56
+ user: ShellUser | null;
57
+ role: string | null;
58
+ nav: NavItem[];
59
+ /** The current pathname, for the active nav item. */
60
+ activeHref: string;
61
+ /** Default "/admin". */
62
+ basePath?: string;
63
+ /** Default `${basePath}/auth/signout`. */
64
+ signOutPath?: string;
65
+ defaultTheme?: Theme;
66
+ children: ReactNode;
67
+ }
68
+ declare function AdminShell({ siteName, logoUrl, user, role, nav, activeHref, basePath, signOutPath, defaultTheme, children, }: AdminShellProps): react.JSX.Element;
69
+
70
+ declare function TopBar({ siteName, logoUrl, homeHref, user, role, theme, onTheme, signOutPath }: {
71
+ siteName: string;
72
+ logoUrl?: string | null;
73
+ homeHref: string;
74
+ user: ShellUser | null;
75
+ role: string | null;
76
+ theme: Theme;
77
+ onTheme: (t: Theme) => void;
78
+ signOutPath: string;
79
+ }): react.JSX.Element;
80
+
81
+ type ButtonVariant = "primary" | "secondary" | "danger" | "ghost";
82
+ type ButtonSize = "sm" | "md";
83
+ declare function buttonClass(variant?: ButtonVariant, size?: ButtonSize, extra?: string): string;
84
+ /** Kept for parity with the tracker's API; returns the class string to spread onto an <a>. */
85
+ declare function buttonStyle(variant?: ButtonVariant, size?: ButtonSize): {
86
+ className: string;
87
+ };
88
+ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
89
+ variant?: ButtonVariant;
90
+ size?: ButtonSize;
91
+ }
92
+ declare function Button({ variant, size, className, type, ...rest }: ButtonProps): react.JSX.Element;
93
+ interface LinkButtonProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
94
+ variant?: ButtonVariant;
95
+ size?: ButtonSize;
96
+ }
97
+ declare function LinkButton({ variant, size, className, ...rest }: LinkButtonProps): react.JSX.Element;
98
+
99
+ declare function Card({ title, meta, children, actions, className }: {
100
+ title?: ReactNode;
101
+ meta?: ReactNode;
102
+ children?: ReactNode;
103
+ actions?: ReactNode;
104
+ className?: string;
105
+ }): react.JSX.Element;
106
+ declare function PageHeader({ eyebrow, title, subtitle, actions }: {
107
+ eyebrow?: ReactNode;
108
+ title: ReactNode;
109
+ subtitle?: ReactNode;
110
+ actions?: ReactNode;
111
+ }): react.JSX.Element;
112
+ type PillTone = "up" | "warn" | "down" | "neutral" | "plain";
113
+ declare function StatusPill({ tone, children, title }: {
114
+ tone?: PillTone;
115
+ children: ReactNode;
116
+ title?: string;
117
+ }): react.JSX.Element;
118
+ declare function EmptyState({ title, children, action }: {
119
+ title: ReactNode;
120
+ children?: ReactNode;
121
+ action?: ReactNode;
122
+ }): react.JSX.Element;
123
+
124
+ /** Seven stroke icons, 18px, currentColor. Registry ids pick one; unknown ids get `doc`. */
125
+ type IconName = "home" | "posts" | "events" | "venues" | "users" | "settings" | "doc";
126
+ declare function Icon({ name }: {
127
+ name: IconName;
128
+ }): react.JSX.Element;
129
+ declare function iconFor(id: string): IconName;
130
+
131
+ /**
132
+ * SHELL_CSS — the design system, as one string the shell injects.
133
+ *
134
+ * Palette borrowed from the visibility tracker (Realiiz's back-office look):
135
+ * warm bone/ink with an operative-green signal. Light is the default so every
136
+ * install looks finished; dark is one attribute away. Everything is scoped to
137
+ * `.rz-admin` so a host site's variables never leak in and ours never leak out.
138
+ *
139
+ * A site overrides by redeclaring any variable on `.rz-admin` — e.g.
140
+ * .rz-admin { --signal: #0a58ca; --rz-font: var(--font-inter), sans-serif; }
141
+ *
142
+ * The block at the end maps the forms-ui variables onto these tokens, so
143
+ * ContentForm and MagicLinkForm re-skin without touching their own code.
144
+ */
145
+ declare const SHELL_CSS = "\n.rz-admin, .rz-admin[data-theme=\"light\"] {\n --ink: #1a1916; --bone: #f5f0e8; --signal: #166534;\n --surface-0: #faf7f2; --surface-1: #f5f0e8; --surface-2: #ede8de; --surface-3: #e2dbd0;\n --text-primary: #1a1916; --text-secondary: #4a4640; --text-muted: #8a8480;\n --signal-dim: #15803d; --signal-glow: #14532d; --signal-warn: #d97706; --series-blue: #2563eb;\n --border: #e2dbd0; --separator: #ede8de;\n --status-up: var(--signal); --status-down: #dc2626; --status-neutral: var(--text-muted); --status-blocked: var(--border);\n --signal-fg: #ffffff;\n}\n.rz-admin[data-theme=\"dark\"] {\n --ink: #e8e4d9; --bone: #1a1a18; --signal: #4ade80;\n --surface-0: #111110; --surface-1: #1a1a18; --surface-2: #252522; --surface-3: #2e2e2a;\n --text-primary: #e8e4d9; --text-secondary: #a8a49a; --text-muted: #6b6762;\n --signal-dim: #22c55e; --signal-glow: #86efac; --signal-warn: #f59e0b; --series-blue: #3b82f6;\n --border: #2e2e2a; --separator: #252522;\n --status-up: var(--signal); --status-down: #f87171; --status-neutral: var(--text-muted); --status-blocked: var(--border);\n --signal-fg: #111110;\n}\n.rz-admin {\n --rz-font: system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n --rz-font-display: var(--rz-font);\n --rz-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;\n --rz-radius: 6px;\n --rz-sidebar: 220px; --rz-sidebar-collapsed: 58px; --rz-topbar: 52px;\n\n /* forms-ui speaks these; map them once so both packages agree */\n --realiiz-form-fg: var(--text-primary); --realiiz-form-muted: var(--text-muted);\n --realiiz-form-border: var(--border); --realiiz-form-accent: var(--signal);\n --realiiz-form-accent-fg: var(--signal-fg); --realiiz-form-danger: var(--status-down);\n --realiiz-form-radius: var(--rz-radius); --realiiz-form-font: var(--rz-font);\n --realiiz-form-focus-ring: 0 0 0 3px color-mix(in srgb, var(--signal) 25%, transparent);\n\n box-sizing: border-box; min-height: 100vh; display: flex; flex-direction: column;\n background: var(--surface-0); color: var(--text-primary);\n font-family: var(--rz-font); font-size: 14px; line-height: 1.45;\n}\n.rz-admin *, .rz-admin *::before, .rz-admin *::after { box-sizing: inherit; }\n.rz-admin a { color: inherit; }\n.rz-admin h1, .rz-admin h2, .rz-admin h3 { font-family: var(--rz-font-display); color: var(--text-primary); margin: 0; }\n.rz-admin .realiiz-form__control { background: var(--surface-0); }\n\n/* ---- frame ---------------------------------------------------------- */\n.rz-topbar { height: var(--rz-topbar); display: flex; align-items: center; gap: 1rem; padding: 0 1.25rem; border-bottom: 1px solid var(--border); background: var(--surface-1); flex-shrink: 0; }\n.rz-topbar__brand { display: flex; align-items: center; gap: .6rem; font-weight: 600; font-size: .9375rem; text-decoration: none; }\n.rz-topbar__logo { height: 22px; width: auto; display: block; }\n.rz-topbar__divider { width: 1px; height: 18px; background: var(--border); }\n.rz-topbar__right { margin-left: auto; display: flex; align-items: center; gap: 1rem; }\n.rz-topbar__greeting { color: var(--text-secondary); font-size: .8rem; }\n.rz-body { display: flex; flex: 1; min-height: 0; }\n.rz-sidebar { width: var(--rz-sidebar); flex-shrink: 0; border-right: 1px solid var(--border); background: var(--surface-1); display: flex; flex-direction: column; transition: width .15s ease; }\n.rz-sidebar--collapsed { width: var(--rz-sidebar-collapsed); }\n.rz-sidebar__nav { padding: .75rem 0; flex: 1; overflow-y: auto; }\n.rz-sidebar__item { display: flex; align-items: center; gap: .65rem; padding: .5rem .9rem; margin: 1px .5rem; border-radius: var(--rz-radius); color: var(--text-secondary); text-decoration: none; font-size: .8125rem; border-left: 2px solid transparent; white-space: nowrap; overflow: hidden; }\n.rz-sidebar__item:hover { background: var(--surface-2); color: var(--text-primary); }\n.rz-sidebar__item--active { background: var(--surface-2); color: var(--text-primary); border-left-color: var(--signal); }\n.rz-sidebar--collapsed .rz-sidebar__label { display: none; }\n.rz-sidebar__collapse { border-top: 1px solid var(--separator); padding: .3rem .5rem; }\n.rz-sidebar__collapse button { width: 100%; }\n.rz-main { flex: 1; min-width: 0; overflow-x: hidden; }\n.rz-content { max-width: 960px; margin: 0 auto; padding: 28px 28px 80px; }\n\n/* ---- primitives ----------------------------------------------------- */\n.rz-btn { font-family: inherit; font-weight: 500; border-radius: var(--rz-radius); cursor: pointer; text-decoration: none; display: inline-flex; align-items: center; justify-content: center; gap: .4rem; white-space: nowrap; line-height: 1.2; }\n.rz-btn--md { padding: .45rem 1rem; font-size: .8125rem; }\n.rz-btn--sm { padding: .3rem .7rem; font-size: .75rem; }\n.rz-btn--primary { background: var(--signal); color: var(--signal-fg); border: 1px solid transparent; }\n.rz-btn--secondary { background: var(--surface-3); color: var(--text-secondary); border: 1px solid var(--border); }\n.rz-btn--danger { background: transparent; color: var(--status-down); border: 1px solid var(--status-down); }\n.rz-btn--ghost { background: transparent; color: var(--text-secondary); border: 1px solid var(--border); }\n.rz-btn:disabled { opacity: .55; cursor: default; }\n.rz-card { background: var(--surface-1); border: 1px solid var(--border); border-radius: 8px; padding: 18px 20px; }\n.rz-card__title { font-size: .9375rem; font-weight: 600; margin-bottom: .35rem; }\n.rz-card__meta { color: var(--text-muted); font-size: .8rem; }\n.rz-card__actions { display: flex; gap: .5rem; margin-top: .9rem; flex-wrap: wrap; }\n.rz-grid { display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }\n.rz-pagehead { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }\n.rz-pagehead__eyebrow { font-family: var(--rz-font-mono); font-size: .6875rem; text-transform: uppercase; letter-spacing: .08em; color: var(--text-muted); margin-bottom: .3rem; }\n.rz-pagehead h1 { font-size: 1.375rem; font-weight: 600; }\n.rz-pagehead__sub { color: var(--text-secondary); font-size: .85rem; margin-top: .25rem; }\n.rz-pill { display: inline-flex; align-items: center; gap: .4rem; font-family: var(--rz-font-mono); font-size: .6875rem; text-transform: uppercase; letter-spacing: .06em; padding: .15rem .5rem; border: 1px solid var(--border); border-radius: 999px; color: var(--text-secondary); }\n.rz-pill::before { content: \"\"; width: 7px; height: 7px; border-radius: 50%; background: var(--status-neutral); }\n.rz-pill--up::before { background: var(--status-up); } .rz-pill--warn::before { background: var(--signal-warn); } .rz-pill--down::before { background: var(--status-down); }\n.rz-pill--plain::before { display: none; }\n.rz-empty { border: 1px dashed var(--border); border-radius: 8px; padding: 32px; text-align: center; color: var(--text-secondary); }\n.rz-empty h3 { font-size: 1rem; margin-bottom: .4rem; }\n.rz-table { width: 100%; border-collapse: collapse; background: var(--surface-1); border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }\n.rz-table th, .rz-table td { text-align: left; padding: 10px 14px; border-bottom: 1px solid var(--separator); font-size: .875rem; vertical-align: top; }\n.rz-table th { font-family: var(--rz-font-mono); font-size: .6875rem; text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted); background: var(--surface-2); }\n.rz-table tr:last-child td { border-bottom: 0; }\n.rz-muted { color: var(--text-muted); }\n.rz-alert { border: 1px solid var(--status-down); color: var(--status-down); border-radius: var(--rz-radius); padding: 12px 14px; margin: 0 0 16px; }\n.rz-ok { border: 1px solid var(--status-up); color: var(--signal-dim); border-radius: var(--rz-radius); padding: 12px 14px; margin: 0 0 16px; }\n\n/* ---- account menu --------------------------------------------------- */\n.rz-avatar { width: 2rem; height: 2rem; border-radius: 50%; background: var(--surface-3); border: 1px solid var(--border); color: var(--text-primary); font-size: .6875rem; font-weight: 600; letter-spacing: .04em; display: flex; align-items: center; justify-content: center; cursor: pointer; font-family: inherit; padding: 0; }\n.rz-menu { position: absolute; right: 0; top: calc(100% + 8px); min-width: 240px; background: var(--surface-1); border: 1px solid var(--border); border-radius: 8px; box-shadow: 0 10px 30px rgba(0,0,0,.12); padding: 6px; z-index: 50; }\n.rz-menu__who { padding: 10px 10px 8px; }\n.rz-menu__name { font-weight: 600; font-size: .875rem; }\n.rz-menu__email { color: var(--text-muted); font-size: .75rem; word-break: break-all; }\n.rz-menu__sep { height: 1px; background: var(--separator); margin: 4px 0; }\n.rz-menu__row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 8px 10px; font-size: .8125rem; color: var(--text-secondary); }\n.rz-menu__signout { padding: 6px; }\n.rz-menu__signout .rz-btn { width: 100%; }\n.rz-seg { display: inline-flex; border: 1px solid var(--border); border-radius: var(--rz-radius); overflow: hidden; }\n.rz-seg button { font: inherit; font-size: .75rem; padding: .25rem .6rem; background: transparent; color: var(--text-secondary); border: 0; cursor: pointer; }\n.rz-seg button[aria-pressed=\"true\"] { background: var(--surface-3); color: var(--text-primary); }\n\n/* ---- mobile: sidebar becomes a strip under the top bar --------------- */\n@media (max-width: 720px) {\n .rz-body { flex-direction: column; }\n .rz-sidebar, .rz-sidebar--collapsed { width: 100%; border-right: 0; border-bottom: 1px solid var(--border); flex-direction: row; }\n .rz-sidebar__nav { display: flex; padding: .35rem .25rem; overflow-x: auto; }\n .rz-sidebar__item { flex-direction: column; gap: .2rem; font-size: .6875rem; padding: .4rem .6rem; border-left: 0; border-bottom: 2px solid transparent; }\n .rz-sidebar__item--active { border-bottom-color: var(--signal); }\n .rz-sidebar--collapsed .rz-sidebar__label { display: block; }\n .rz-sidebar__collapse { display: none; }\n .rz-topbar__greeting { display: none; }\n .rz-content { padding: 18px 14px 60px; }\n}\n";
146
+
147
+ export { AccountMenu, AdminShell, type AdminShellProps, Button, type ButtonSize, type ButtonVariant, Card, EmptyState, Icon, type IconName, LinkButton, type NavItem, PageHeader, type PillTone, SHELL_CSS, type ShellUser, Sidebar, StatusPill, type Theme, ThemeToggle, TopBar, buttonClass, buttonStyle, firstNameOf, iconFor, initialsOf, isActive, useTheme };