@xenide-io/the-old-ui-theme 0.3.2 → 0.4.3

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 (62) hide show
  1. package/dist/chunk-54ZR4VL5.mjs +4641 -0
  2. package/dist/chunk-G53YLHVE.mjs +1023 -0
  3. package/dist/{index-BgG8Homu.d.mts → index-B5NOyoKS.d.mts} +100 -100
  4. package/dist/{index-BgG8Homu.d.ts → index-B5NOyoKS.d.ts} +100 -100
  5. package/dist/index.d.mts +9 -145
  6. package/dist/index.d.ts +9 -145
  7. package/dist/index.js +653 -1111
  8. package/dist/index.mjs +86 -342
  9. package/dist/suite.d.mts +760 -0
  10. package/dist/suite.d.ts +760 -0
  11. package/dist/suite.js +2800 -0
  12. package/dist/suite.mjs +2441 -0
  13. package/dist/ui.d.mts +1 -2
  14. package/dist/ui.d.ts +1 -2
  15. package/dist/ui.js +14 -10
  16. package/dist/ui.mjs +13 -11
  17. package/package.json +43 -24
  18. package/src/components/icons/icons.tsx +0 -2
  19. package/src/components/icons/index.ts +0 -3
  20. package/src/components/sections/IconShowcase.tsx +1 -110
  21. package/src/components/sections/NewComponentsShowcase.tsx +4 -4
  22. package/src/components/sections/SidebarShowcase.tsx +3 -3
  23. package/src/components/sections/SuiteShowcase.tsx +669 -0
  24. package/src/components/ui/ComponentDocs.tsx +2 -2
  25. package/src/components/ui/ContextMenu.tsx +1 -1
  26. package/src/components/ui/Menubar.tsx +1 -1
  27. package/src/components/ui/Sidebar.tsx +10 -8
  28. package/src/styles/suite-skin.css +255 -0
  29. package/src/suite/components/ai-panel.tsx +202 -0
  30. package/src/suite/components/app-switcher.tsx +196 -0
  31. package/src/suite/components/command-palette-host.tsx +62 -0
  32. package/src/suite/components/deferred-chrome.tsx +12 -0
  33. package/src/suite/components/suite-app-layout.tsx +69 -0
  34. package/src/suite/components/suite-bottom-nav.tsx +113 -0
  35. package/src/suite/components/suite-layout.tsx +285 -0
  36. package/src/suite/components/suite-mobile-drawer.tsx +150 -0
  37. package/src/suite/components/suite-mobile-header.tsx +71 -0
  38. package/src/suite/components/suite-notification-bell.tsx +227 -0
  39. package/src/suite/components/suite-settings-mobile-nav.tsx +83 -0
  40. package/src/suite/components/suite-sidebar.tsx +198 -0
  41. package/src/suite/components/suite-skeleton.tsx +191 -0
  42. package/src/suite/components/suite-user-menu.tsx +109 -0
  43. package/src/suite/components/theme-provider.tsx +174 -0
  44. package/src/suite/components/today-calibrating.tsx +95 -0
  45. package/src/suite/components/today-page-frame.tsx +28 -0
  46. package/src/suite/components/today-ui.tsx +370 -0
  47. package/src/suite/icons/app-accents.ts +75 -0
  48. package/src/suite/icons/glyph-parts.tsx +67 -0
  49. package/src/suite/icons/glyphs.ts +203 -0
  50. package/src/suite/icons/index.ts +12 -0
  51. package/src/suite/icons/suite-app-icon.tsx +68 -0
  52. package/src/suite/icons/suite-icon.tsx +93 -0
  53. package/src/suite/index.ts +108 -0
  54. package/src/suite/lib/apps.ts +75 -0
  55. package/src/suite/lib/cn.ts +6 -0
  56. package/src/suite/lib/injected.ts +54 -0
  57. package/src/suite/lib/motion.tsx +48 -0
  58. package/src/suite/lib/use-idle-mount.ts +25 -0
  59. package/dist/chunk-5HSOBJ4F.mjs +0 -5968
  60. package/src/components/icons/lemon-brand-icons.tsx +0 -16
  61. package/src/components/icons/lemon-category-icons.tsx +0 -72
  62. package/src/components/icons/lemon-icons.tsx +0 -57
@@ -0,0 +1,669 @@
1
+ "use client";
2
+
3
+ import { useState, type ComponentPropsWithoutRef } from "react";
4
+ import Image from "next/image";
5
+ import {
6
+ NavArrowDown as ChevronDown,
7
+ Home,
8
+ Calendar,
9
+ Search,
10
+ Bell,
11
+ User,
12
+ Settings,
13
+ LogOut,
14
+ Menu,
15
+ Xmark,
16
+ Plus,
17
+ } from "iconoir-react";
18
+
19
+ import { Button, DropdownMenu, DropdownItem, CommandPalette, ShowcaseWrapper, Spinner } from "@/components/ui";
20
+ import {
21
+ AppSwitcher,
22
+ AppSwitcherMark,
23
+ AppSwitcherChevron,
24
+ type SuiteAppEntry,
25
+ } from "@/suite/components/app-switcher";
26
+ import { CommandPaletteHost } from "@/suite/components/command-palette-host";
27
+ import type { SuiteCommandItem } from "@/suite/lib/injected";
28
+ import { SuiteMobileHeader } from "@/suite/components/suite-mobile-header";
29
+ import { SuiteBottomNav, type SuiteBottomNavItem } from "@/suite/components/suite-bottom-nav";
30
+ import { SuiteUserMenu } from "@/suite/components/suite-user-menu";
31
+ import {
32
+ SuiteNotificationBell,
33
+ type SuiteNotification,
34
+ type SuiteNotificationsResponse,
35
+ } from "@/suite/components/suite-notification-bell";
36
+ import { SuiteMobileDrawer } from "@/suite/components/suite-mobile-drawer";
37
+ import { SuiteSettingsMobileNav } from "@/suite/components/suite-settings-mobile-nav";
38
+ import { SuiteSkeleton, SuiteSkeletonCard, SuiteSkeletonList } from "@/suite/components/suite-skeleton";
39
+ import {
40
+ SuitePage,
41
+ SuitePageHeader,
42
+ SuiteToolbar,
43
+ SuiteTabList,
44
+ SuiteSectionHeader,
45
+ } from "@/suite/components/suite-layout";
46
+ import { TodayPageFrame } from "@/suite/components/today-page-frame";
47
+ import { TodayCalibrating } from "@/suite/components/today-calibrating";
48
+ import { SuiteSidebar, type SuiteSidebarNavItem } from "@/suite/components/suite-sidebar";
49
+ import { SuiteAppLayout } from "@/suite/components/suite-app-layout";
50
+ import {
51
+ SuiteIcon,
52
+ SUITE_ICON_NAMES,
53
+ APP_ACCENTS,
54
+ type SuiteAccentSlug,
55
+ } from "@/suite/icons";
56
+
57
+ const REAL_APPS = ["kraken", "shellstack", "tides", "turtletime"] as const;
58
+ type RealAppSlug = (typeof REAL_APPS)[number];
59
+
60
+ const APP_ICON_SRC: Record<RealAppSlug, string> = {
61
+ turtletime: "/turtletime-icon.svg",
62
+ tides: "/tides-icon.svg",
63
+ kraken: "/kraken-icon.svg",
64
+ shellstack: "/shellstack-icon.svg",
65
+ };
66
+
67
+ const ICON_SIZE = 44;
68
+
69
+ const SUITE_APPS: SuiteAppEntry[] = REAL_APPS.map((app) => ({
70
+ slug: app,
71
+ name: APP_ACCENTS[app].label,
72
+ description: `${APP_ACCENTS[app].label} app`,
73
+ icon: APP_ICON_SRC[app],
74
+ }));
75
+
76
+ const BOTTOM_NAV_ITEMS: SuiteBottomNavItem[] = [
77
+ { href: "#suite-today", label: "Today", icon: Home, active: true },
78
+ { href: "#suite-track", label: "Track", icon: Calendar },
79
+ { href: "#suite-reports", label: "Reports", icon: Search },
80
+ ];
81
+
82
+ const SETTINGS_ITEMS = [
83
+ { href: "#profile", label: "Profile", icon: User },
84
+ { href: "#account", label: "Account", icon: Settings },
85
+ { href: "#notifications", label: "Notifications", icon: Bell },
86
+ ];
87
+
88
+ const SIDEBAR_NAV_ITEMS: SuiteSidebarNavItem[] = [
89
+ { href: "#suite-today", label: "Today", icon: Home, active: true },
90
+ { href: "#suite-track", label: "Tracker", icon: Calendar },
91
+ { href: "#suite-reports", label: "Reports", icon: Search },
92
+ { href: "#suite-settings", label: "Settings", icon: Settings },
93
+ ];
94
+
95
+ const NOTIFICATIONS: SuiteNotification[] = [
96
+ {
97
+ id: "1",
98
+ title: "New document shared",
99
+ body: "Alex shared the Q3 roadmap with you.",
100
+ href: "#",
101
+ kind: "document",
102
+ source_app: "kraken",
103
+ read_at: null,
104
+ created_at: new Date(Date.now() - 5 * 60 * 1000).toISOString(),
105
+ workspace: "ShellStack",
106
+ organisation: "Xenide",
107
+ },
108
+ {
109
+ id: "2",
110
+ title: "Task assigned",
111
+ body: "Review TurtleTime weekly report.",
112
+ href: "#",
113
+ kind: "task",
114
+ source_app: "tides",
115
+ read_at: new Date().toISOString(),
116
+ created_at: new Date(Date.now() - 60 * 60 * 1000).toISOString(),
117
+ workspace: "ShellStack",
118
+ organisation: "Xenide",
119
+ },
120
+ ];
121
+
122
+ const PALETTE_ITEMS: SuiteCommandItem[] = [
123
+ { id: "today", label: "Go to Today", icon: <Home className="h-4 w-4" />, onSelect: () => {} },
124
+ { id: "search", label: "Search", icon: <Search className="h-4 w-4" />, onSelect: () => {} },
125
+ { id: "settings", label: "Settings", icon: <Settings className="h-4 w-4" />, onSelect: () => {} },
126
+ ];
127
+
128
+ function DemoAppIcon({ app }: { app: RealAppSlug }) {
129
+ return (
130
+ <div className="relative h-8 w-8 overflow-hidden rounded-lg">
131
+ <Image
132
+ src={APP_ICON_SRC[app]}
133
+ alt={APP_ACCENTS[app].label}
134
+ width={32}
135
+ height={32}
136
+ className="h-full w-full"
137
+ unoptimized
138
+ />
139
+ </div>
140
+ );
141
+ }
142
+
143
+ function InjectedDropdownItem(props: ComponentPropsWithoutRef<"button">) {
144
+ return <DropdownItem {...props} />;
145
+ }
146
+
147
+ export default function SuiteShowcase() {
148
+ const [currentApp, setCurrentApp] = useState<string>("turtletime");
149
+ const [drawerOpen, setDrawerOpen] = useState(false);
150
+ const [settingsHref, setSettingsHref] = useState("#profile");
151
+ const [paletteOpen, setPaletteOpen] = useState(false);
152
+
153
+ return (
154
+ <ShowcaseWrapper
155
+ id="suite"
156
+ title="Suite"
157
+ description="The shared ShellStack chrome — app switcher, mobile header, bottom nav, account menu, notification bell, command palette, drawer, settings nav, skeletons, page layout, and icon system."
158
+ code={`import {
159
+ AppSwitcher, SuiteMobileHeader, SuiteBottomNav,
160
+ SuiteUserMenu, SuiteNotificationBell, CommandPaletteHost,
161
+ SuiteMobileDrawer, SuiteSettingsMobileNav,
162
+ SuiteSkeleton, SuitePage, SuitePageHeader,
163
+ } from "@xenide-io/the-old-ui-theme/suite";`}
164
+ filename="SuiteExample.tsx"
165
+ >
166
+ <div className="space-y-8">
167
+ {/* App icons */}
168
+ <section className="ph-panel space-y-4">
169
+ <div>
170
+ <h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Actual app icons</h3>
171
+ <p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
172
+ The real favicons shipped by each app — not generated suite glyphs.
173
+ </p>
174
+ </div>
175
+ <div className="flex flex-wrap items-center gap-6 rounded-lg border border-ph-border-subtle bg-ph-raised p-6">
176
+ {REAL_APPS.map((app) => (
177
+ <div key={app} className="flex flex-col items-center gap-2">
178
+ <div className="relative h-11 w-11 overflow-hidden rounded-xl">
179
+ <Image
180
+ src={APP_ICON_SRC[app]}
181
+ alt={APP_ACCENTS[app].label}
182
+ width={ICON_SIZE}
183
+ height={ICON_SIZE}
184
+ className="h-full w-full"
185
+ unoptimized
186
+ />
187
+ </div>
188
+ <span className="text-xs font-medium text-ph-subtle">{APP_ACCENTS[app].label}</span>
189
+ </div>
190
+ ))}
191
+ </div>
192
+ </section>
193
+
194
+ {/* App switcher */}
195
+ <section className="ph-panel space-y-4">
196
+ <div>
197
+ <h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">App switcher</h3>
198
+ <p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
199
+ Dropdown app launcher with app mark, title, and collapsible rail mode.
200
+ </p>
201
+ </div>
202
+ <div className="flex flex-wrap items-start gap-6 rounded-lg border border-ph-border-subtle bg-ph-raised p-6">
203
+ <div className="w-64">
204
+ <AppSwitcher
205
+ apps={SUITE_APPS}
206
+ currentApp={currentApp}
207
+ onSelect={(entry) => setCurrentApp(entry.slug)}
208
+ mark={<DemoAppIcon app={currentApp as RealAppSlug} />}
209
+ title={
210
+ <span className="flex min-w-0 flex-1 flex-col">
211
+ <span className="truncate text-sm font-semibold text-ph-ink">{APP_ACCENTS[currentApp as RealAppSlug].label}</span>
212
+ <span className="truncate text-xs text-ph-mutedtext">ShellStack workspace</span>
213
+ </span>
214
+ }
215
+ dropdownMenu={DropdownMenu}
216
+ dropdownItem={InjectedDropdownItem}
217
+ />
218
+ </div>
219
+ <div className="flex flex-col items-center gap-2">
220
+ <span className="text-xs text-ph-mutedtext">Collapsed rail</span>
221
+ <AppSwitcher
222
+ apps={SUITE_APPS}
223
+ currentApp={currentApp}
224
+ onSelect={(entry) => setCurrentApp(entry.slug)}
225
+ mark={<DemoAppIcon app={currentApp as RealAppSlug} />}
226
+ title={null}
227
+ collapsed
228
+ dropdownMenu={DropdownMenu}
229
+ dropdownItem={InjectedDropdownItem}
230
+ />
231
+ </div>
232
+ </div>
233
+ </section>
234
+
235
+ {/* Sidebar + app layout */}
236
+ <section className="ph-panel space-y-4">
237
+ <div>
238
+ <h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Standardised app shell</h3>
239
+ <p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
240
+ SuiteSidebar + SuiteAppLayout — the same sidebar and responsive shell every app should use. TurtleTime-style: primary nav + account footer.
241
+ </p>
242
+ </div>
243
+ <div className="overflow-hidden rounded-lg border border-ph-border-subtle bg-ph-raised h-[420px]">
244
+ <div className="hidden lg:block h-full">
245
+ <SuiteAppLayout
246
+ sidebarWidth={220}
247
+ sidebar={
248
+ <SuiteSidebar
249
+ appSwitcher={
250
+ <AppSwitcher
251
+ apps={SUITE_APPS}
252
+ currentApp={currentApp}
253
+ onSelect={(entry) => setCurrentApp(entry.slug)}
254
+ mark={<DemoAppIcon app={currentApp as RealAppSlug} />}
255
+ title={
256
+ <span className="flex min-w-0 flex-col">
257
+ <span className="truncate text-sm font-semibold text-ph-ink">{APP_ACCENTS[currentApp as RealAppSlug].label}</span>
258
+ <span className="truncate text-xs text-ph-mutedtext">ShellStack workspace</span>
259
+ </span>
260
+ }
261
+ dropdownMenu={DropdownMenu}
262
+ dropdownItem={InjectedDropdownItem}
263
+ />
264
+ }
265
+ contextSwitcher={
266
+ <div className="flex items-center gap-2 rounded-lg border border-ph-border bg-ph-surface px-2 py-1.5 text-sm text-ph-ink">
267
+ <span className="h-5 w-5 rounded bg-ph-brand text-[10px] font-bold text-[var(--ph-on-accent)] flex items-center justify-center">S</span>
268
+ ShellStack
269
+ </div>
270
+ }
271
+ navItems={SIDEBAR_NAV_ITEMS}
272
+ notificationBell={
273
+ <Button variant="ghost" size="sm" icon={<Bell className="h-4 w-4" />} aria-label="Notifications" />
274
+ }
275
+ userMenu={
276
+ <SuiteUserMenu
277
+ name="Jane Doe"
278
+ email="jane@xenide.io"
279
+ settingsHref="#settings"
280
+ onSignOut={() => {}}
281
+ />
282
+ }
283
+ />
284
+ }
285
+ >
286
+ <div className="p-6 text-sm text-ph-subtle">
287
+ Main content area. On desktop the sidebar is fixed; on mobile it collapses to the suite mobile chrome.
288
+ </div>
289
+ </SuiteAppLayout>
290
+ </div>
291
+ <div className="flex flex-col lg:hidden h-full">
292
+ <SuiteMobileHeader
293
+ onMenuClick={() => setDrawerOpen((v) => !v)}
294
+ title={
295
+ <div className="flex items-center gap-2">
296
+ <DemoAppIcon app={currentApp as RealAppSlug} />
297
+ <span className="font-semibold text-ph-ink">{APP_ACCENTS[currentApp as RealAppSlug].label}</span>
298
+ </div>
299
+ }
300
+ actions={
301
+ <>
302
+ <Button variant="ghost" size="sm" icon={<Bell className="h-4 w-4" />} aria-label="Notifications" />
303
+ <div className="h-8 w-8 rounded-full bg-ph-brand text-xs font-bold text-[var(--ph-on-accent)] flex items-center justify-center">JD</div>
304
+ </>
305
+ }
306
+ />
307
+ <main className="flex-1 p-4 text-sm text-ph-subtle">
308
+ Mobile content area.
309
+ </main>
310
+ <SuiteBottomNav items={BOTTOM_NAV_ITEMS} />
311
+ </div>
312
+ </div>
313
+ </section>
314
+
315
+ {/* Sidebar with secondary nav / project tree */}
316
+ <section className="ph-panel space-y-4">
317
+ <div>
318
+ <h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Sidebar with project tree</h3>
319
+ <p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
320
+ Tides / Kraken pattern: primary nav above, secondary project/document tree below, and a mobile browse button that opens the same tree in a drawer.
321
+ </p>
322
+ </div>
323
+ <div className="overflow-hidden rounded-lg border border-ph-border-subtle bg-ph-raised h-[420px]">
324
+ <div className="hidden lg:block h-full">
325
+ <SuiteAppLayout
326
+ sidebarWidth={240}
327
+ sidebar={
328
+ <SuiteSidebar
329
+ appSwitcher={
330
+ <AppSwitcher
331
+ apps={SUITE_APPS}
332
+ currentApp={currentApp}
333
+ onSelect={(entry) => setCurrentApp(entry.slug)}
334
+ mark={<DemoAppIcon app={currentApp as RealAppSlug} />}
335
+ title={
336
+ <span className="flex min-w-0 flex-col">
337
+ <span className="truncate text-sm font-semibold text-ph-ink">{APP_ACCENTS[currentApp as RealAppSlug].label}</span>
338
+ <span className="truncate text-xs text-ph-mutedtext">ShellStack workspace</span>
339
+ </span>
340
+ }
341
+ dropdownMenu={DropdownMenu}
342
+ dropdownItem={InjectedDropdownItem}
343
+ />
344
+ }
345
+ contextSwitcher={
346
+ <div className="flex items-center gap-2 rounded-lg border border-ph-border bg-ph-surface px-2 py-1.5 text-sm text-ph-ink">
347
+ <span className="h-5 w-5 rounded bg-ph-brand text-[10px] font-bold text-[var(--ph-on-accent)] flex items-center justify-center">W</span>
348
+ Workspace
349
+ </div>
350
+ }
351
+ navItems={SIDEBAR_NAV_ITEMS}
352
+ secondaryNav={
353
+ <div className="space-y-1">
354
+ <p className="px-2 pb-1 text-[10px] font-semibold uppercase tracking-wider text-ph-mutedtext">Projects</p>
355
+ <button type="button" className="flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-sm text-ph-subtle hover:bg-ph-muted">
356
+ <span className="h-2 w-2 rounded-full bg-ph-brand" />
357
+ Acme Corp
358
+ </button>
359
+ <button type="button" className="flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-sm text-ph-subtle hover:bg-ph-muted">
360
+ <span className="h-2 w-2 rounded-full bg-ph-accent" />
361
+ Globex
362
+ </button>
363
+ </div>
364
+ }
365
+ notificationBell={
366
+ <Button variant="ghost" size="sm" icon={<Bell className="h-4 w-4" />} aria-label="Notifications" />
367
+ }
368
+ userMenu={
369
+ <SuiteUserMenu
370
+ name="Jane Doe"
371
+ email="jane@xenide.io"
372
+ settingsHref="#settings"
373
+ onSignOut={() => {}}
374
+ />
375
+ }
376
+ />
377
+ }
378
+ >
379
+ <div className="p-6 text-sm text-ph-subtle">
380
+ Desktop content area. The project tree lives inside the sidebar via the <code className="rounded bg-ph-muted px-1 py-0.5 text-xs">secondaryNav</code> prop.
381
+ </div>
382
+ </SuiteAppLayout>
383
+ </div>
384
+ <div className="flex flex-col lg:hidden h-full">
385
+ <SuiteMobileHeader
386
+ title={
387
+ <div className="flex items-center gap-2">
388
+ <DemoAppIcon app={currentApp as RealAppSlug} />
389
+ <span className="font-semibold text-ph-ink">{APP_ACCENTS[currentApp as RealAppSlug].label}</span>
390
+ <Button
391
+ variant="ghost"
392
+ size="sm"
393
+ onClick={() => setDrawerOpen(true)}
394
+ icon={<Search className="h-4 w-4" />}
395
+ aria-label="Browse projects"
396
+ />
397
+ </div>
398
+ }
399
+ actions={
400
+ <>
401
+ <Button variant="ghost" size="sm" icon={<Bell className="h-4 w-4" />} aria-label="Notifications" />
402
+ <div className="h-8 w-8 rounded-full bg-ph-brand text-xs font-bold text-[var(--ph-on-accent)] flex items-center justify-center">JD</div>
403
+ </>
404
+ }
405
+ />
406
+ <main className="flex-1 p-4 text-sm text-ph-subtle">
407
+ Mobile content area. Tap the search icon in the header to open the project tree drawer.
408
+ </main>
409
+ <SuiteBottomNav items={BOTTOM_NAV_ITEMS} />
410
+ <SuiteMobileDrawer open={drawerOpen} onClose={() => setDrawerOpen(false)} showCloseButton dialogLabel="Browse projects" side="right">
411
+ <div className="p-4 space-y-1">
412
+ <p className="px-2 pb-1 text-[10px] font-semibold uppercase tracking-wider text-ph-mutedtext">Projects</p>
413
+ <button type="button" className="flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-sm text-ph-subtle hover:bg-ph-muted">
414
+ <span className="h-2 w-2 rounded-full bg-ph-brand" />
415
+ Acme Corp
416
+ </button>
417
+ <button type="button" className="flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-sm text-ph-subtle hover:bg-ph-muted">
418
+ <span className="h-2 w-2 rounded-full bg-ph-accent" />
419
+ Globex
420
+ </button>
421
+ </div>
422
+ </SuiteMobileDrawer>
423
+ </div>
424
+ </div>
425
+ </section>
426
+
427
+ {/* Mobile header + bottom nav */}
428
+ <section className="ph-panel space-y-4">
429
+ <div>
430
+ <h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Mobile chrome</h3>
431
+ <p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
432
+ SuiteMobileHeader and SuiteBottomNav — the standard mobile shell used by every app.
433
+ </p>
434
+ </div>
435
+ <div className="space-y-4 rounded-lg border border-ph-border-subtle bg-ph-raised p-4">
436
+ <SuiteMobileHeader
437
+ onMenuClick={() => setDrawerOpen((v) => !v)}
438
+ title={
439
+ <div className="flex items-center gap-2">
440
+ <DemoAppIcon app="turtletime" />
441
+ <span className="font-semibold text-ph-ink">TurtleTime</span>
442
+ </div>
443
+ }
444
+ actions={
445
+ <>
446
+ <Button variant="ghost" size="sm" icon={<Bell className="h-4 w-4" />} aria-label="Notifications" />
447
+ <div className="h-8 w-8 rounded-full bg-ph-brand text-xs font-bold text-[var(--ph-on-accent)] flex items-center justify-center">JD</div>
448
+ </>
449
+ }
450
+ />
451
+ <div className="rounded-lg border border-ph-border bg-ph-surface p-4 text-sm text-ph-subtle">
452
+ Page content area
453
+ </div>
454
+ <SuiteBottomNav items={BOTTOM_NAV_ITEMS} />
455
+ </div>
456
+ </section>
457
+
458
+ {/* User menu + notification bell */}
459
+ <section className="ph-panel space-y-4">
460
+ <div>
461
+ <h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Account & notifications</h3>
462
+ <p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
463
+ SuiteUserMenu and SuiteNotificationBell with mock data.
464
+ </p>
465
+ </div>
466
+ <div className="flex flex-wrap items-start gap-6 rounded-lg border border-ph-border-subtle bg-ph-raised p-6">
467
+ <SuiteUserMenu
468
+ name="Jane Doe"
469
+ email="jane@xenide.io"
470
+ settingsHref="#settings"
471
+ onSignOut={() => {}}
472
+ />
473
+ <SuiteNotificationBell
474
+ fetchNotifications={async () => ({
475
+ notifications: NOTIFICATIONS,
476
+ unread_count: 1,
477
+ })}
478
+ markRead={async () => {}}
479
+ markAllRead={async () => {}}
480
+ dropdownMenu={DropdownMenu}
481
+ />
482
+ </div>
483
+ </section>
484
+
485
+ {/* Command palette */}
486
+ <section className="ph-panel space-y-4">
487
+ <div>
488
+ <h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Command palette</h3>
489
+ <p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
490
+ Cmd/Ctrl+K host wired to the theme CommandPalette. Press the button below to open.
491
+ </p>
492
+ </div>
493
+ <div className="rounded-lg border border-ph-border-subtle bg-ph-raised p-6">
494
+ <CommandPaletteHost
495
+ items={PALETTE_ITEMS}
496
+ commandPalette={CommandPalette}
497
+ />
498
+ <Button
499
+ variant="secondary"
500
+ size="sm"
501
+ onClick={() => setPaletteOpen(true)}
502
+ className="mt-4"
503
+ >
504
+ Open command palette
505
+ </Button>
506
+ <CommandPalette
507
+ items={PALETTE_ITEMS}
508
+ isOpen={paletteOpen}
509
+ onClose={() => setPaletteOpen(false)}
510
+ />
511
+ </div>
512
+ </section>
513
+
514
+ {/* Mobile drawer */}
515
+ <section className="ph-panel space-y-4">
516
+ <div>
517
+ <h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Mobile drawer</h3>
518
+ <p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
519
+ Slide-in panel with backdrop, Escape close, and body scroll-lock.
520
+ </p>
521
+ </div>
522
+ <div className="rounded-lg border border-ph-border-subtle bg-ph-raised p-6">
523
+ <Button variant="secondary" size="sm" onClick={() => setDrawerOpen(true)}>
524
+ Open drawer
525
+ </Button>
526
+ <SuiteMobileDrawer open={drawerOpen} onClose={() => setDrawerOpen(false)} showCloseButton dialogLabel="Demo navigation">
527
+ <div className="p-4 space-y-2">
528
+ <a href="#suite" className="block rounded-lg px-3 py-2 text-sm text-ph-ink hover:bg-ph-muted">Today</a>
529
+ <a href="#suite" className="block rounded-lg px-3 py-2 text-sm text-ph-ink hover:bg-ph-muted">Track time</a>
530
+ <a href="#suite" className="block rounded-lg px-3 py-2 text-sm text-ph-ink hover:bg-ph-muted">Reports</a>
531
+ </div>
532
+ </SuiteMobileDrawer>
533
+ </div>
534
+ </section>
535
+
536
+ {/* Settings mobile nav */}
537
+ <section className="ph-panel space-y-4">
538
+ <div>
539
+ <h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Settings mobile nav</h3>
540
+ <p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
541
+ Horizontal scrollable icon+label pills for mobile settings.
542
+ </p>
543
+ </div>
544
+ <div className="rounded-lg border border-ph-border-subtle bg-ph-raised p-4">
545
+ <SuiteSettingsMobileNav
546
+ items={SETTINGS_ITEMS.map((item) => ({ ...item, icon: item.icon }))}
547
+ activeHref={settingsHref}
548
+ onSelect={(href) => setSettingsHref(href)}
549
+ />
550
+ </div>
551
+ </section>
552
+
553
+ {/* Skeletons */}
554
+ <section className="ph-panel space-y-4">
555
+ <div>
556
+ <h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Skeletons</h3>
557
+ <p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
558
+ Shared loading placeholders using the theme shimmer.
559
+ </p>
560
+ </div>
561
+ <div className="grid gap-4 rounded-lg border border-ph-border-subtle bg-ph-raised p-6 sm:grid-cols-2 lg:grid-cols-3">
562
+ <SuiteSkeleton lines={3} />
563
+ <SuiteSkeletonCard rows={3} />
564
+ <SuiteSkeletonList items={3} />
565
+ </div>
566
+ </section>
567
+
568
+ {/* Page layout primitives */}
569
+ <section className="ph-panel space-y-4">
570
+ <div>
571
+ <h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Page layout primitives</h3>
572
+ <p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
573
+ SuitePage, SuitePageHeader, SuiteToolbar, SuiteTabList, and SuiteSectionHeader.
574
+ </p>
575
+ </div>
576
+ <div className="rounded-lg border border-ph-border-subtle bg-ph-raised p-6">
577
+ <SuitePage width="content" inset className="rounded-xl border border-ph-border bg-ph-surface shadow-sm">
578
+ <SuitePageHeader
579
+ eyebrow="Workspace"
580
+ title="Projects"
581
+ description="Manage client work across the suite."
582
+ actions={
583
+ <SuiteToolbar>
584
+ <Button variant="secondary" size="sm">Filter</Button>
585
+ <Button variant="primary" size="sm" icon={<Plus className="h-3.5 w-3.5" />}>New</Button>
586
+ </SuiteToolbar>
587
+ }
588
+ />
589
+ <SuiteTabList label="Project views" className="mt-6">
590
+ <Button variant="ghost" size="sm">Board</Button>
591
+ <Button variant="ghost" size="sm">List</Button>
592
+ <Button variant="ghost" size="sm">Calendar</Button>
593
+ </SuiteTabList>
594
+ <SuiteSectionHeader
595
+ title="Active projects"
596
+ description="3 projects with work this week"
597
+ action={<Button variant="ghost" size="sm">View all</Button>}
598
+ className="mt-6"
599
+ />
600
+ </SuitePage>
601
+ </div>
602
+ </section>
603
+
604
+ {/* Today frame */}
605
+ <section className="ph-panel space-y-4">
606
+ <div>
607
+ <h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Today page chrome</h3>
608
+ <p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
609
+ TodayPageFrame and TodayCalibrating loading state.
610
+ </p>
611
+ </div>
612
+ <div className="grid gap-4 rounded-lg border border-ph-border-subtle bg-ph-raised p-6 sm:grid-cols-2">
613
+ <div className="h-48 overflow-hidden rounded-xl">
614
+ <TodayPageFrame>
615
+ <div className="p-6 text-sm text-ph-ink">Today page content goes here.</div>
616
+ </TodayPageFrame>
617
+ </div>
618
+ <div className="flex h-48 items-center justify-center rounded-xl border border-ph-border bg-ph-surface">
619
+ <TodayCalibrating spinner={Spinner} />
620
+ </div>
621
+ </div>
622
+ </section>
623
+
624
+ {/* Suite glyphs */}
625
+ <section className="ph-panel space-y-4">
626
+ <div>
627
+ <h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Suite glyphs</h3>
628
+ <p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
629
+ 24×24 strokes that inherit currentColor and accept a per-app accent tint.
630
+ </p>
631
+ </div>
632
+ <div className="grid grid-cols-3 gap-3 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8">
633
+ {SUITE_ICON_NAMES.map((name) => (
634
+ <div
635
+ key={name}
636
+ className="flex min-w-0 flex-col items-center gap-2 rounded-lg border border-ph-border-subtle bg-ph-raised p-3 text-center"
637
+ >
638
+ <div className="flex h-8 items-center text-ph-ink">
639
+ <SuiteIcon name={name} size={24} />
640
+ </div>
641
+ <span className="max-w-full break-words text-[10px] font-medium tracking-wide text-ph-mutedtext">
642
+ {name}
643
+ </span>
644
+ </div>
645
+ ))}
646
+ </div>
647
+ </section>
648
+
649
+ {/* Accent tints */}
650
+ <section className="ph-panel space-y-4">
651
+ <div>
652
+ <h3 className="text-xs font-semibold uppercase tracking-wider text-ph-mutedtext">Accent tints</h3>
653
+ <p className="mt-2 max-w-3xl text-sm leading-relaxed text-ph-subtle">
654
+ The same glyph with each app accent applied via CSS custom property.
655
+ </p>
656
+ </div>
657
+ <div className="flex flex-wrap items-center gap-6 rounded-lg border border-ph-border-subtle bg-ph-raised p-6">
658
+ {REAL_APPS.map((app) => (
659
+ <div key={app} className="flex flex-col items-center gap-2">
660
+ <SuiteIcon name="today-sun" accent={app} size={32} />
661
+ <span className="text-xs font-medium text-ph-subtle">{APP_ACCENTS[app].label}</span>
662
+ </div>
663
+ ))}
664
+ </div>
665
+ </section>
666
+ </div>
667
+ </ShowcaseWrapper>
668
+ );
669
+ }