@xenide-io/the-old-ui-theme 0.2.6 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/chunk-FDMD3IIN.mjs +3831 -0
- package/dist/index-D1RTT2Ap.d.mts +1044 -0
- package/dist/index-D1RTT2Ap.d.ts +1044 -0
- package/dist/index.d.mts +11 -758
- package/dist/index.d.ts +11 -758
- package/dist/index.js +1749 -768
- package/dist/index.mjs +312 -2846
- package/dist/tailwind-preset.js +7 -0
- package/dist/tailwind-preset.mjs +7 -0
- package/dist/ui.d.mts +4 -0
- package/dist/ui.d.ts +4 -0
- package/dist/ui.js +3864 -0
- package/dist/ui.mjs +169 -0
- package/package.json +28 -6
- package/src/app/globals.css +1005 -227
- package/src/components/charts/index.ts +8 -0
- package/src/components/charts/ph-insight-charts.tsx +162 -0
- package/src/components/dashboard/DashboardFiltersBar.tsx +19 -0
- package/src/components/dashboard/DashboardGrid.tsx +23 -0
- package/src/components/dashboard/DashboardInsightPlaceholder.tsx +37 -0
- package/src/components/dashboard/DashboardKpiCard.tsx +25 -0
- package/src/components/dashboard/DashboardToolbar.tsx +23 -0
- package/src/components/dashboard/DashboardWell.tsx +10 -0
- package/src/components/dashboard/InsightMiniCard.tsx +26 -0
- package/src/components/dashboard/InsightShell.tsx +37 -0
- package/src/components/dashboard/InsightShellHeader.tsx +22 -0
- package/src/components/dashboard/MiniTrendBars.tsx +51 -0
- package/src/components/dashboard/index.ts +31 -0
- package/src/components/dashboard/types.ts +9 -0
- package/src/components/icons/IconBase.tsx +47 -0
- package/src/components/icons/createIconBase.tsx +32 -0
- package/src/components/icons/icons.tsx +557 -0
- package/src/components/icons/index.ts +3 -0
- package/src/components/sections/AccordionShowcase.tsx +45 -0
- package/src/components/sections/AlertShowcase.tsx +40 -0
- package/src/components/sections/AvatarShowcase.tsx +80 -0
- package/src/components/sections/BadgeShowcase.tsx +65 -0
- package/src/components/sections/ButtonShowcase.tsx +308 -0
- package/src/components/sections/CalendarShowcase.tsx +35 -0
- package/src/components/sections/CardShowcase.tsx +159 -0
- package/src/components/sections/ChartShowcase.tsx +190 -0
- package/src/components/sections/CodeMockupShowcase.tsx +56 -0
- package/src/components/sections/ColorPalette.tsx +117 -0
- package/src/components/sections/CommandPaletteShowcase.tsx +48 -0
- package/src/components/sections/CountdownShowcase.tsx +43 -0
- package/src/components/sections/DashboardShowcase.tsx +191 -0
- package/src/components/sections/DiffShowcase.tsx +70 -0
- package/src/components/sections/DrawerShowcase.tsx +97 -0
- package/src/components/sections/DropdownShowcase.tsx +98 -0
- package/src/components/sections/EmptyStateShowcase.tsx +50 -0
- package/src/components/sections/FilterChipsShowcase.tsx +98 -0
- package/src/components/sections/FormShowcase.tsx +127 -0
- package/src/components/sections/HoverCardShowcase.tsx +50 -0
- package/src/components/sections/IconShowcase.tsx +127 -0
- package/src/components/sections/IndicatorShowcase.tsx +52 -0
- package/src/components/sections/KbdShowcase.tsx +57 -0
- package/src/components/sections/ModalShowcase.tsx +211 -0
- package/src/components/sections/NavigationShowcase.tsx +199 -0
- package/src/components/sections/ProductLayoutsShowcase.tsx +138 -0
- package/src/components/sections/ProgressShowcase.tsx +82 -0
- package/src/components/sections/SegmentedControlShowcase.tsx +49 -0
- package/src/components/sections/SetupThemeShowcase.tsx +262 -0
- package/src/components/sections/StackShowcase.tsx +33 -0
- package/src/components/sections/StatShowcase.tsx +129 -0
- package/src/components/sections/StepperShowcase.tsx +37 -0
- package/src/components/sections/SwapShowcase.tsx +99 -0
- package/src/components/sections/TabShowcase.tsx +84 -0
- package/src/components/sections/TableShowcase.tsx +140 -0
- package/src/components/sections/TimelineShowcase.tsx +83 -0
- package/src/components/sections/ToastShowcase.tsx +108 -0
- package/src/components/sections/TooltipShowcase.tsx +44 -0
- package/src/components/sections/UtilityShowcase.tsx +81 -0
- package/src/components/ui/Accordion.tsx +77 -0
- package/src/components/ui/Alert.tsx +77 -0
- package/src/components/ui/AuthLayout.tsx +58 -0
- package/src/components/ui/Avatar.tsx +97 -0
- package/src/components/ui/Badge.tsx +60 -0
- package/src/components/ui/Button.tsx +120 -0
- package/src/components/ui/ButtonChrome.tsx +20 -0
- package/src/components/ui/Calendar.tsx +115 -0
- package/src/components/ui/Card.tsx +70 -0
- package/src/components/ui/ChatBubble.tsx +71 -0
- package/src/components/ui/CodeBlock.tsx +48 -0
- package/src/components/ui/CollapsibleSection.tsx +49 -0
- package/src/components/ui/CommandPalette.tsx +137 -0
- package/src/components/ui/ComponentDocs.tsx +52 -0
- package/src/components/ui/Drawer.tsx +71 -0
- package/src/components/ui/DropdownMenu.tsx +262 -0
- package/src/components/ui/EmptyState.tsx +33 -0
- package/src/components/ui/FilterBar.tsx +165 -0
- package/src/components/ui/FilterChips.tsx +80 -0
- package/src/components/ui/FilterControls.tsx +30 -0
- package/src/components/ui/FormField.tsx +69 -0
- package/src/components/ui/HoverCard.tsx +36 -0
- package/src/components/ui/Indicator.tsx +61 -0
- package/src/components/ui/Input.tsx +385 -0
- package/src/components/ui/Kbd.tsx +113 -0
- package/src/components/ui/Loader.tsx +96 -0
- package/src/components/ui/Modal.tsx +105 -0
- package/src/components/ui/Navigation.tsx +94 -0
- package/src/components/ui/Panel.tsx +18 -0
- package/src/components/ui/Progress.tsx +59 -0
- package/src/components/ui/Rating.tsx +65 -0
- package/src/components/ui/SearchInput.tsx +106 -0
- package/src/components/ui/SegmentedControl.tsx +44 -0
- package/src/components/ui/SettingsLayout.tsx +94 -0
- package/src/components/ui/ShowcaseWrapper.tsx +124 -0
- package/src/components/ui/Stat.tsx +39 -0
- package/src/components/ui/Stepper.tsx +74 -0
- package/src/components/ui/Table.tsx +55 -0
- package/src/components/ui/Tabs.tsx +53 -0
- package/src/components/ui/Terminal.tsx +54 -0
- package/src/components/ui/ThemeDomSync.tsx +17 -0
- package/src/components/ui/ThemeManager.tsx +18 -0
- package/src/components/ui/ThemeSwitcher.tsx +46 -0
- package/src/components/ui/Timeline.tsx +60 -0
- package/src/components/ui/Toast.tsx +70 -0
- package/src/components/ui/Typography.tsx +129 -0
- package/src/components/ui/first-slice.test.tsx +98 -0
- package/src/components/ui/index.ts +151 -0
- package/src/components/ui/interaction-primitives.test.tsx +77 -0
- package/src/components/ui/kbd-icons.test.tsx +90 -0
- package/src/components/ui/product-patterns.test.tsx +77 -0
- package/src/styles/themes.css +528 -0
- package/tailwind-preset.ts +7 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AuthCard,
|
|
3
|
+
AuthDivider,
|
|
4
|
+
AuthLayout,
|
|
5
|
+
Badge,
|
|
6
|
+
Button,
|
|
7
|
+
Checkbox,
|
|
8
|
+
Input,
|
|
9
|
+
LoadingState,
|
|
10
|
+
Panel,
|
|
11
|
+
SettingsLayout,
|
|
12
|
+
SettingsNav,
|
|
13
|
+
ShowcaseWrapper,
|
|
14
|
+
Toggle,
|
|
15
|
+
} from "@/components/ui";
|
|
16
|
+
import { IconDataReel, IconMail, IconPerson, IconTuning } from "@/components/icons";
|
|
17
|
+
|
|
18
|
+
const settingsGroups = [
|
|
19
|
+
{
|
|
20
|
+
label: "Workspace",
|
|
21
|
+
items: [
|
|
22
|
+
{ id: "general", label: "General", href: "#settings", icon: <IconTuning className="h-4 w-4" aria-hidden />, active: true },
|
|
23
|
+
{ id: "members", label: "Members", href: "#settings", icon: <IconPerson className="h-4 w-4" aria-hidden />, badge: "12" },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
label: "Data",
|
|
28
|
+
items: [
|
|
29
|
+
{ id: "sources", label: "Sources", href: "#settings", icon: <IconDataReel className="h-4 w-4" aria-hidden /> },
|
|
30
|
+
{ id: "email", label: "Email reports", href: "#settings", icon: <IconMail className="h-4 w-4" aria-hidden /> },
|
|
31
|
+
],
|
|
32
|
+
},
|
|
33
|
+
] as const;
|
|
34
|
+
|
|
35
|
+
export default function ProductLayoutsShowcase() {
|
|
36
|
+
const authCode = `import { AuthCard, AuthDivider, AuthLayout, Button, Input } from "@xenide-io/the-old-ui-theme";
|
|
37
|
+
|
|
38
|
+
<AuthLayout brand="Acme Analytics" footer="Privacy · Terms">
|
|
39
|
+
<AuthCard title="Welcome back" description="Sign in to your workspace">
|
|
40
|
+
<Input label="Work email" type="email" autoComplete="email" />
|
|
41
|
+
<Input label="Password" type="password" autoComplete="current-password" />
|
|
42
|
+
<Button variant="primary" className="w-full">Sign in</Button>
|
|
43
|
+
<AuthDivider />
|
|
44
|
+
<Button variant="secondary" className="w-full">Continue with SSO</Button>
|
|
45
|
+
</AuthCard>
|
|
46
|
+
</AuthLayout>`;
|
|
47
|
+
|
|
48
|
+
const settingsCode = `import { SettingsLayout, SettingsNav } from "@xenide-io/the-old-ui-theme";
|
|
49
|
+
|
|
50
|
+
<SettingsLayout
|
|
51
|
+
title="Workspace settings"
|
|
52
|
+
navigation={<SettingsNav groups={settingsGroups} />}
|
|
53
|
+
>
|
|
54
|
+
{/* Route-specific settings panels */}
|
|
55
|
+
</SettingsLayout>`;
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<>
|
|
59
|
+
<ShowcaseWrapper
|
|
60
|
+
id="auth"
|
|
61
|
+
title="Authentication layouts"
|
|
62
|
+
description="Focused sign-in, verification, recovery, and loading shells. Authentication state and provider logic stay application-owned."
|
|
63
|
+
code={authCode}
|
|
64
|
+
filename="SignInPage.tsx"
|
|
65
|
+
>
|
|
66
|
+
<div className="grid gap-5 xl:grid-cols-[1.35fr,0.65fr]">
|
|
67
|
+
<AuthLayout
|
|
68
|
+
className="min-h-[34rem]"
|
|
69
|
+
brand={<span>The Old UI</span>}
|
|
70
|
+
aside={
|
|
71
|
+
<div className="flex h-full flex-col justify-between">
|
|
72
|
+
<strong className="text-xl">Understand every product decision.</strong>
|
|
73
|
+
<p className="max-w-xs text-sm opacity-80">One focused authentication step at a time, with room for product context.</p>
|
|
74
|
+
</div>
|
|
75
|
+
}
|
|
76
|
+
footer={<>Privacy · Terms · Status</>}
|
|
77
|
+
>
|
|
78
|
+
<AuthCard
|
|
79
|
+
title="Welcome back"
|
|
80
|
+
description="Sign in to your workspace"
|
|
81
|
+
footer={<>New to the workspace? <a href="#auth" className="font-medium text-ph-brand">Create an account</a></>}
|
|
82
|
+
>
|
|
83
|
+
<Input label="Work email" type="email" autoComplete="email" placeholder="you@company.com" />
|
|
84
|
+
<Input label="Password" type="password" autoComplete="current-password" />
|
|
85
|
+
<div className="flex items-center justify-between gap-3">
|
|
86
|
+
<Checkbox label="Remember me" />
|
|
87
|
+
<a href="#auth" className="text-xs font-medium text-ph-brand">Forgot password?</a>
|
|
88
|
+
</div>
|
|
89
|
+
<Button variant="primary" className="w-full">Sign in</Button>
|
|
90
|
+
<AuthDivider />
|
|
91
|
+
<Button variant="secondary" className="w-full">Continue with SSO</Button>
|
|
92
|
+
</AuthCard>
|
|
93
|
+
</AuthLayout>
|
|
94
|
+
|
|
95
|
+
<div className="grid gap-5">
|
|
96
|
+
<AuthCard title="Check your inbox" description="We sent a six-digit code to alex@company.com">
|
|
97
|
+
<Input label="Verification code" inputMode="numeric" autoComplete="one-time-code" placeholder="000000" />
|
|
98
|
+
<Button variant="primary" className="w-full">Verify email</Button>
|
|
99
|
+
</AuthCard>
|
|
100
|
+
<AuthCard title="Preparing workspace" description="Authentication completed successfully.">
|
|
101
|
+
<LoadingState
|
|
102
|
+
label="Preparing your workspace"
|
|
103
|
+
title="Loading your workspace"
|
|
104
|
+
description="Connecting data sources and restoring your last view."
|
|
105
|
+
variant="dots"
|
|
106
|
+
/>
|
|
107
|
+
</AuthCard>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
</ShowcaseWrapper>
|
|
111
|
+
|
|
112
|
+
<ShowcaseWrapper
|
|
113
|
+
id="settings"
|
|
114
|
+
title="Settings layout"
|
|
115
|
+
description="Route-friendly grouped navigation, mobile disclosure menu, page actions, and flexible settings content."
|
|
116
|
+
code={settingsCode}
|
|
117
|
+
filename="SettingsPage.tsx"
|
|
118
|
+
>
|
|
119
|
+
<SettingsLayout
|
|
120
|
+
title="Workspace settings"
|
|
121
|
+
description="Manage identity, data collection, and workspace defaults."
|
|
122
|
+
actions={<Button variant="primary" size="sm">Save changes</Button>}
|
|
123
|
+
navigation={<SettingsNav groups={settingsGroups} />}
|
|
124
|
+
>
|
|
125
|
+
<Panel title="Workspace identity" description="Shown in navigation, reports, and invitations." className="space-y-4">
|
|
126
|
+
<Input label="Workspace name" defaultValue="Acme Analytics" />
|
|
127
|
+
<Input label="Workspace URL" defaultValue="app.example.com/acme" />
|
|
128
|
+
</Panel>
|
|
129
|
+
<Panel title="Collection defaults" className="space-y-4">
|
|
130
|
+
<Toggle label="Capture page views automatically" defaultChecked />
|
|
131
|
+
<Toggle label="Mask sensitive text inputs" defaultChecked />
|
|
132
|
+
<div><Badge variant="warning">Restart required</Badge></div>
|
|
133
|
+
</Panel>
|
|
134
|
+
</SettingsLayout>
|
|
135
|
+
</ShowcaseWrapper>
|
|
136
|
+
</>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ComponentDocs,
|
|
3
|
+
Loader,
|
|
4
|
+
LoadingState,
|
|
5
|
+
Progress,
|
|
6
|
+
ShowcaseWrapper,
|
|
7
|
+
Skeleton,
|
|
8
|
+
} from "@/components/ui";
|
|
9
|
+
|
|
10
|
+
export default function ProgressShowcase() {
|
|
11
|
+
const code = `import { Loader, LoadingState, Progress, Skeleton } from "@xenide-io/the-old-ui-theme";
|
|
12
|
+
|
|
13
|
+
<Progress label="Ingest backlog" value={72} showPercentage />
|
|
14
|
+
<Loader variant="spinner" label="Refreshing insight" />
|
|
15
|
+
<Loader variant="dots" label="Loading events" />
|
|
16
|
+
<Loader variant="pulse" label="Connecting" />
|
|
17
|
+
<LoadingState title="Loading dashboard" description="Restoring the latest results." />
|
|
18
|
+
<div role="status" aria-label="Loading account">
|
|
19
|
+
<Skeleton shape="title" />
|
|
20
|
+
<Skeleton shape="text" />
|
|
21
|
+
<Skeleton shape="short-text" />
|
|
22
|
+
</div>`;
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<ShowcaseWrapper
|
|
26
|
+
title="Progress & loading"
|
|
27
|
+
description="Determinate progress, compact action loaders, full-region loading states, and structure-matching cold-load skeletons."
|
|
28
|
+
code={code}
|
|
29
|
+
filename="LoadingExample.tsx"
|
|
30
|
+
docs={
|
|
31
|
+
<ComponentDocs
|
|
32
|
+
rows={[
|
|
33
|
+
{ name: "Loader.variant", type: "spinner | dots | pulse", defaultValue: "spinner", description: "Indeterminate treatment for compact asynchronous work." },
|
|
34
|
+
{ name: "Loader.size", type: "sm | md | lg", defaultValue: "md", description: "Visual loader size." },
|
|
35
|
+
{ name: "LoadingState", type: "LoadingStateProps", description: "Centered region state with one accessible status message." },
|
|
36
|
+
{ name: "Skeleton.shape", type: "title | text | short-text | avatar | button | block", defaultValue: "text", description: "Decorative skeleton matching expected content geometry." },
|
|
37
|
+
{ name: "Progress.value", type: "number", description: "Real determinate progress value." },
|
|
38
|
+
]}
|
|
39
|
+
/>
|
|
40
|
+
}
|
|
41
|
+
>
|
|
42
|
+
<div className="space-y-6">
|
|
43
|
+
<div className="ph-panel space-y-6">
|
|
44
|
+
<h3 className="text-sm font-semibold uppercase tracking-wider text-ph-mutedtext">Determinate progress</h3>
|
|
45
|
+
<Progress label="Ingest backlog" value={72} color="bg-ph-brand" showPercentage />
|
|
46
|
+
<Progress label="Exports" value={45} color="bg-ph-blue" showPercentage />
|
|
47
|
+
<Progress label="LLM evaluations" value={28} color="bg-ph-purple" showPercentage />
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
<div className="grid gap-4 md:grid-cols-3">
|
|
51
|
+
{(["spinner", "dots", "pulse"] as const).map((variant) => (
|
|
52
|
+
<div key={variant} className="ph-panel flex min-h-28 flex-col items-center justify-center gap-3">
|
|
53
|
+
<Loader variant={variant} size="lg" label={`${variant} loader`} />
|
|
54
|
+
<code className="ph-kbd">{variant}</code>
|
|
55
|
+
</div>
|
|
56
|
+
))}
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<div className="grid gap-4 lg:grid-cols-2">
|
|
60
|
+
<div className="ph-panel p-0">
|
|
61
|
+
<LoadingState
|
|
62
|
+
label="Loading dashboard"
|
|
63
|
+
title="Loading dashboard"
|
|
64
|
+
description="Restoring the latest results without pretending progress is measurable."
|
|
65
|
+
/>
|
|
66
|
+
</div>
|
|
67
|
+
<div className="ph-panel" role="status" aria-label="Loading account summary">
|
|
68
|
+
<div className="mb-5 flex items-center gap-3">
|
|
69
|
+
<Skeleton shape="avatar" />
|
|
70
|
+
<div className="min-w-0 flex-1">
|
|
71
|
+
<Skeleton shape="title" className="mb-2 w-1/2" />
|
|
72
|
+
<Skeleton shape="short-text" className="w-2/3" />
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
<Skeleton shape="block" className="mb-4" />
|
|
76
|
+
<div className="flex justify-end"><Skeleton shape="button" /></div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</ShowcaseWrapper>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { ComponentDocs, SegmentedControl, ShowcaseWrapper } from "@/components/ui";
|
|
5
|
+
import { IconGridView, IconListView } from "@/components/icons";
|
|
6
|
+
|
|
7
|
+
export default function SegmentedControlShowcase() {
|
|
8
|
+
const [view, setView] = useState("grid");
|
|
9
|
+
|
|
10
|
+
const options = [
|
|
11
|
+
{ value: "grid", label: "Grid", icon: <IconGridView className="h-4 w-4" /> },
|
|
12
|
+
{ value: "list", label: "List", icon: <IconListView className="h-4 w-4" /> },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
const code = `import { SegmentedControl } from "the-old-ui";
|
|
16
|
+
import { IconGridView, IconListView } from "the-old-ui";
|
|
17
|
+
|
|
18
|
+
<div className="ph-panel">
|
|
19
|
+
<SegmentedControl options={options} value={view} onChange={setView} />
|
|
20
|
+
<div className="mt-4 rounded-lg bg-ph-muted p-4 text-sm text-ph-subtle">
|
|
21
|
+
Selected: <span className="font-semibold text-ph-ink">{view}</span>
|
|
22
|
+
</div>
|
|
23
|
+
</div>`;
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<ShowcaseWrapper
|
|
27
|
+
title="Segmented Control"
|
|
28
|
+
description="Compact controlled switch for mutually exclusive display modes."
|
|
29
|
+
code={code}
|
|
30
|
+
filename="SegmentedControlExample.tsx"
|
|
31
|
+
docs={
|
|
32
|
+
<ComponentDocs
|
|
33
|
+
rows={[
|
|
34
|
+
{ name: "options", type: "{ value; label; icon? }[]", description: "Segments to render." },
|
|
35
|
+
{ name: "value", type: "string", description: "Currently selected value." },
|
|
36
|
+
{ name: "onChange", type: "(value: string) => void", description: "Called when a segment is selected." },
|
|
37
|
+
]}
|
|
38
|
+
/>
|
|
39
|
+
}
|
|
40
|
+
>
|
|
41
|
+
<div className="ph-panel">
|
|
42
|
+
<SegmentedControl options={options} value={view} onChange={setView} />
|
|
43
|
+
<div className="mt-4 rounded-lg bg-ph-muted p-4 text-sm text-ph-subtle">
|
|
44
|
+
Selected: <span className="font-semibold text-ph-ink">{view}</span>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</ShowcaseWrapper>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useMemo, useState } from "react";
|
|
4
|
+
import { CodeBlock, H3, P, Panel, ThemeSwitcher } from "@/components/ui";
|
|
5
|
+
import { THEMES } from "@/themes";
|
|
6
|
+
|
|
7
|
+
const themePairs = [
|
|
8
|
+
{ name: "HedgeHog", light: "hedgehog-light", dark: "hedgehog-dark" },
|
|
9
|
+
{ name: "Sheets", light: "sheets", dark: "sheets-dark" },
|
|
10
|
+
{ name: "Note", light: "note", dark: "note-dark" },
|
|
11
|
+
{ name: "Presentation", light: "presentation", dark: "presentation-dark" },
|
|
12
|
+
{ name: "Socials", light: "socials", dark: "socials-dark" },
|
|
13
|
+
{ name: "Chats", light: "chats-light", dark: "chats-dark" },
|
|
14
|
+
{ name: "Catppuccin", light: "catppuccin-light", dark: "catppuccin-dark" },
|
|
15
|
+
{ name: "Xenide", light: "xenide-light", dark: "xenide-dark" },
|
|
16
|
+
{ name: "GitHub", light: "github-light", dark: "github-dark" },
|
|
17
|
+
{ name: "Rose Pine", light: "rosepine-light", dark: "rosepine-dark" },
|
|
18
|
+
{ name: "TurtleTime", light: "turtletime", dark: "turtletime-dark" },
|
|
19
|
+
{ name: "Bikini Bottom", light: "bikini-bottom", dark: "bikini-bottom-dark" },
|
|
20
|
+
{ name: "Notion", light: "notion", dark: "notion-dark" },
|
|
21
|
+
] as const;
|
|
22
|
+
|
|
23
|
+
const guideTokens = [
|
|
24
|
+
{ label: "canvas", token: "--ph-canvas", text: "text-ph-ink" },
|
|
25
|
+
{ label: "surface", token: "--ph-surface", text: "text-ph-ink" },
|
|
26
|
+
{ label: "muted", token: "--ph-muted", text: "text-ph-ink" },
|
|
27
|
+
{ label: "toolbar", token: "--ph-toolbar", text: "text-ph-ink" },
|
|
28
|
+
{ label: "border", token: "--ph-border", text: "text-ph-ink" },
|
|
29
|
+
{ label: "accent", token: "--ph-accent", text: "text-white" },
|
|
30
|
+
{ label: "blue", token: "--ph-blue", text: "text-white" },
|
|
31
|
+
{ label: "purple", token: "--ph-purple", text: "text-white" },
|
|
32
|
+
{ label: "success", token: "--ph-success", text: "text-white" },
|
|
33
|
+
{ label: "warning", token: "--ph-warning", text: "text-white" },
|
|
34
|
+
{ label: "danger", token: "--ph-danger", text: "text-white" },
|
|
35
|
+
{ label: "info", token: "--ph-info", text: "text-white" },
|
|
36
|
+
] as const;
|
|
37
|
+
|
|
38
|
+
const dataTokens = [1, 2, 3, 4, 5, 6, 7].map((item) => ({
|
|
39
|
+
label: `data ${item}`,
|
|
40
|
+
token: `--ph-data-${item}`,
|
|
41
|
+
text: "text-white",
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
const installCode = `bun add the-old-ui
|
|
45
|
+
# or
|
|
46
|
+
npm install the-old-ui`;
|
|
47
|
+
|
|
48
|
+
const layoutCode = `// app/layout.tsx
|
|
49
|
+
import "the-old-ui/styles.css";
|
|
50
|
+
import { THEME_INIT_SCRIPT, ThemeManager } from "the-old-ui";
|
|
51
|
+
|
|
52
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
53
|
+
return (
|
|
54
|
+
<html lang="en" data-theme="hedgehog-light" suppressHydrationWarning>
|
|
55
|
+
<head>
|
|
56
|
+
<script dangerouslySetInnerHTML={{ __html: THEME_INIT_SCRIPT }} />
|
|
57
|
+
</head>
|
|
58
|
+
<body>
|
|
59
|
+
<ThemeManager>{children}</ThemeManager>
|
|
60
|
+
</body>
|
|
61
|
+
</html>
|
|
62
|
+
);
|
|
63
|
+
}`;
|
|
64
|
+
|
|
65
|
+
const tailwindCode = `// tailwind.config.ts
|
|
66
|
+
import theOldUiPreset from "the-old-ui/tailwind-preset";
|
|
67
|
+
|
|
68
|
+
export default {
|
|
69
|
+
presets: [theOldUiPreset],
|
|
70
|
+
content: [
|
|
71
|
+
"./src/**/*.{ts,tsx}",
|
|
72
|
+
"./app/**/*.{ts,tsx}",
|
|
73
|
+
"./node_modules/the-old-ui/dist/**/*.{js,mjs}",
|
|
74
|
+
],
|
|
75
|
+
};`;
|
|
76
|
+
|
|
77
|
+
const switchCode = `import { ThemeSwitcher, persistTheme } from "the-old-ui";
|
|
78
|
+
|
|
79
|
+
<ThemeSwitcher />
|
|
80
|
+
|
|
81
|
+
// Or switch manually
|
|
82
|
+
persistTheme("sheets-dark");`;
|
|
83
|
+
|
|
84
|
+
const customThemeCode = `/* app/globals.css */
|
|
85
|
+
@import "the-old-ui/styles.css";
|
|
86
|
+
|
|
87
|
+
[data-theme="my-theme-light"] {
|
|
88
|
+
color-scheme: light;
|
|
89
|
+
|
|
90
|
+
--ph-canvas: #f8f5ef;
|
|
91
|
+
--ph-surface: #ffffff;
|
|
92
|
+
--ph-muted: #eee8dd;
|
|
93
|
+
--ph-toolbar: #e5dccd;
|
|
94
|
+
--ph-border: #d7cbb8;
|
|
95
|
+
--ph-border-strong: #a99b87;
|
|
96
|
+
|
|
97
|
+
--ph-text-primary: #15110c;
|
|
98
|
+
--ph-text-secondary: #4d4337;
|
|
99
|
+
--ph-text-tertiary: #796f63;
|
|
100
|
+
|
|
101
|
+
--ph-accent: #f97316;
|
|
102
|
+
--ph-accent-hover: #ea580c;
|
|
103
|
+
--ph-blue: #2563eb;
|
|
104
|
+
--ph-purple: #7c3aed;
|
|
105
|
+
--ph-success: #15803d;
|
|
106
|
+
--ph-warning: #b45309;
|
|
107
|
+
--ph-danger: #dc2626;
|
|
108
|
+
--ph-info: #2563eb;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
[data-theme="my-theme-dark"] {
|
|
112
|
+
color-scheme: dark;
|
|
113
|
+
|
|
114
|
+
--ph-canvas: #11100e;
|
|
115
|
+
--ph-surface: #191713;
|
|
116
|
+
--ph-muted: #242018;
|
|
117
|
+
--ph-toolbar: #2e281f;
|
|
118
|
+
--ph-border: #443a2b;
|
|
119
|
+
--ph-border-strong: #7a6a54;
|
|
120
|
+
|
|
121
|
+
--ph-text-primary: #fff7ed;
|
|
122
|
+
--ph-text-secondary: #d8c8b6;
|
|
123
|
+
--ph-text-tertiary: #a89985;
|
|
124
|
+
|
|
125
|
+
--ph-accent: #fb923c;
|
|
126
|
+
--ph-accent-hover: #fdba74;
|
|
127
|
+
--ph-blue: #60a5fa;
|
|
128
|
+
--ph-purple: #a78bfa;
|
|
129
|
+
--ph-success: #4ade80;
|
|
130
|
+
--ph-warning: #facc15;
|
|
131
|
+
--ph-danger: #f87171;
|
|
132
|
+
--ph-info: #60a5fa;
|
|
133
|
+
}`;
|
|
134
|
+
|
|
135
|
+
const registryCode = `// Add to src/themes/registry.ts if this repo should show it in ThemeSwitcher
|
|
136
|
+
{
|
|
137
|
+
id: "my-theme-light",
|
|
138
|
+
name: "My Theme Light",
|
|
139
|
+
description: "Custom light palette.",
|
|
140
|
+
colorScheme: "light",
|
|
141
|
+
group: "Custom",
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
id: "my-theme-dark",
|
|
145
|
+
name: "My Theme Dark",
|
|
146
|
+
description: "Custom dark palette.",
|
|
147
|
+
colorScheme: "dark",
|
|
148
|
+
group: "Custom",
|
|
149
|
+
}`;
|
|
150
|
+
|
|
151
|
+
export default function SetupThemeShowcase() {
|
|
152
|
+
const [activeTheme, setActiveTheme] = useState("hedgehog-light");
|
|
153
|
+
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
const root = document.documentElement;
|
|
156
|
+
const update = () => setActiveTheme(root.getAttribute("data-theme") || "hedgehog-light");
|
|
157
|
+
update();
|
|
158
|
+
|
|
159
|
+
const observer = new MutationObserver(update);
|
|
160
|
+
observer.observe(root, { attributes: true, attributeFilter: ["data-theme"] });
|
|
161
|
+
return () => observer.disconnect();
|
|
162
|
+
}, []);
|
|
163
|
+
|
|
164
|
+
const activeThemeCode = useMemo(() => {
|
|
165
|
+
const theme = THEMES.find((item) => item.id === activeTheme);
|
|
166
|
+
return `// Current selected theme in this demo\n${JSON.stringify({
|
|
167
|
+
id: activeTheme,
|
|
168
|
+
name: theme?.name ?? activeTheme,
|
|
169
|
+
colorScheme: theme?.colorScheme ?? "light",
|
|
170
|
+
group: theme?.group ?? "Custom",
|
|
171
|
+
}, null, 2)}`;
|
|
172
|
+
}, [activeTheme]);
|
|
173
|
+
|
|
174
|
+
const themeListCode = useMemo(() => {
|
|
175
|
+
return `export const themePairs = ${JSON.stringify(themePairs, null, 2)} as const;`;
|
|
176
|
+
}, []);
|
|
177
|
+
|
|
178
|
+
return (
|
|
179
|
+
<div className="space-y-8">
|
|
180
|
+
<Panel className="space-y-4">
|
|
181
|
+
<H3>Setup</H3>
|
|
182
|
+
<P tone="subtle">
|
|
183
|
+
This demo is also the docs. A consuming app imports the package CSS once, adds the Tailwind preset, then switches palettes through <code className="ph-kbd">html[data-theme]</code>.
|
|
184
|
+
</P>
|
|
185
|
+
<div className="grid gap-4 lg:grid-cols-2">
|
|
186
|
+
<CodeBlock code={installCode} filename="install.sh" language="bash" />
|
|
187
|
+
<CodeBlock code={tailwindCode} filename="tailwind.config.ts" />
|
|
188
|
+
</div>
|
|
189
|
+
<CodeBlock code={layoutCode} filename="app/layout.tsx" />
|
|
190
|
+
</Panel>
|
|
191
|
+
|
|
192
|
+
<Panel className="space-y-4">
|
|
193
|
+
<div className="flex flex-wrap items-center justify-between gap-4">
|
|
194
|
+
<div>
|
|
195
|
+
<H3>Theme Switching</H3>
|
|
196
|
+
<P tone="subtle">Each default palette is a light/dark pair. Pick one below and copy the active theme ID.</P>
|
|
197
|
+
</div>
|
|
198
|
+
<ThemeSwitcher />
|
|
199
|
+
</div>
|
|
200
|
+
<div className="grid gap-4 lg:grid-cols-2">
|
|
201
|
+
<CodeBlock code={activeThemeCode} filename="current-theme.ts" />
|
|
202
|
+
<CodeBlock code={switchCode} filename="ThemeSwitch.tsx" />
|
|
203
|
+
</div>
|
|
204
|
+
</Panel>
|
|
205
|
+
|
|
206
|
+
<Panel className="space-y-4">
|
|
207
|
+
<H3>Default Theme Pairs</H3>
|
|
208
|
+
<P tone="subtle">Use these IDs with <code className="ph-kbd">data-theme</code> or <code className="ph-kbd">persistTheme()</code>.</P>
|
|
209
|
+
<CodeBlock code={themeListCode} filename="theme-pairs.ts" />
|
|
210
|
+
</Panel>
|
|
211
|
+
|
|
212
|
+
<Panel className="space-y-5">
|
|
213
|
+
<div>
|
|
214
|
+
<H3>Theme Color Guide</H3>
|
|
215
|
+
<P tone="subtle">
|
|
216
|
+
Every built-in theme is rendered below with its own <code className="ph-kbd">data-theme</code> attribute. These swatches are the guide for what each token becomes in that theme.
|
|
217
|
+
</P>
|
|
218
|
+
</div>
|
|
219
|
+
<div className="grid gap-4 lg:grid-cols-2">
|
|
220
|
+
{THEMES.map((theme) => (
|
|
221
|
+
<div
|
|
222
|
+
key={theme.id}
|
|
223
|
+
data-theme={theme.id}
|
|
224
|
+
className="rounded-xl border border-ph-border bg-ph-canvas p-4 shadow-ph"
|
|
225
|
+
>
|
|
226
|
+
<div className="mb-3 flex items-baseline justify-between gap-3">
|
|
227
|
+
<div>
|
|
228
|
+
<h4 className="font-semibold text-ph-ink">{theme.name}</h4>
|
|
229
|
+
<p className="text-xs text-ph-mutedtext">{theme.id}</p>
|
|
230
|
+
</div>
|
|
231
|
+
<span className="rounded-full border border-ph-border bg-ph-surface px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-ph-mutedtext">
|
|
232
|
+
{theme.colorScheme}
|
|
233
|
+
</span>
|
|
234
|
+
</div>
|
|
235
|
+
<div className="grid grid-cols-3 gap-2 sm:grid-cols-4">
|
|
236
|
+
{[...guideTokens, ...dataTokens].map((item) => (
|
|
237
|
+
<div
|
|
238
|
+
key={item.token}
|
|
239
|
+
className={`rounded-lg border border-ph-border p-2 ${item.text}`}
|
|
240
|
+
style={{ background: `var(${item.token})` }}
|
|
241
|
+
>
|
|
242
|
+
<div className="text-[10px] font-bold uppercase tracking-wide">{item.label}</div>
|
|
243
|
+
<div className="mt-1 truncate font-mono text-[9px] opacity-80">{item.token}</div>
|
|
244
|
+
</div>
|
|
245
|
+
))}
|
|
246
|
+
</div>
|
|
247
|
+
</div>
|
|
248
|
+
))}
|
|
249
|
+
</div>
|
|
250
|
+
</Panel>
|
|
251
|
+
|
|
252
|
+
<Panel className="space-y-4">
|
|
253
|
+
<H3>Custom Light & Dark Theme</H3>
|
|
254
|
+
<P tone="subtle">Custom themes are DaisyUI-style: define one light token block and one dark token block. Components read semantic tokens, not hardcoded colours.</P>
|
|
255
|
+
<div className="grid gap-4 lg:grid-cols-2">
|
|
256
|
+
<CodeBlock code={customThemeCode} filename="custom-theme.css" language="css" />
|
|
257
|
+
<CodeBlock code={registryCode} filename="registry.ts" />
|
|
258
|
+
</div>
|
|
259
|
+
</Panel>
|
|
260
|
+
</div>
|
|
261
|
+
);
|
|
262
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ShowcaseWrapper } from "@/components/ui";
|
|
2
|
+
|
|
3
|
+
export default function StackShowcase() {
|
|
4
|
+
const code = `
|
|
5
|
+
|
|
6
|
+
<div className="rounded-xl border border-ph-border bg-ph-muted p-10">
|
|
7
|
+
<div className="relative mx-auto h-44 w-48">
|
|
8
|
+
<div className="absolute inset-3 translate-x-4 translate-y-4 rounded-xl border border-ph-border bg-ph-toolbar shadow-ph ring-8 ring-ph-muted" />
|
|
9
|
+
<div className="absolute inset-y-7 left-8 right-[-1rem] translate-x-1 rounded-xl border border-ph-border bg-ph-blue text-center text-xs font-semibold uppercase leading-[4.75rem] text-white shadow-ph ring-8 ring-ph-muted">
|
|
10
|
+
Replay
|
|
11
|
+
</div>
|
|
12
|
+
<div className="absolute inset-x-5 top-0 flex h-[5.75rem] items-center justify-center rounded-xl border border-ph-border bg-ph-surface shadow-ph-md ring-8 ring-ph-muted">
|
|
13
|
+
<span className="text-sm font-bold text-ph-brand">Trends insight</span>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
</div>`;
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<ShowcaseWrapper title="Stacked surfaces" code={code} filename="StackExample.tsx">
|
|
20
|
+
<div className="rounded-xl border border-ph-border bg-ph-muted p-10">
|
|
21
|
+
<div className="relative mx-auto h-44 w-48">
|
|
22
|
+
<div className="absolute inset-3 translate-x-4 translate-y-4 rounded-xl border border-ph-border bg-ph-toolbar shadow-ph ring-8 ring-ph-muted" />
|
|
23
|
+
<div className="absolute inset-y-7 left-8 right-[-1rem] translate-x-1 rounded-xl border border-ph-border bg-ph-blue text-center text-xs font-semibold uppercase leading-[4.75rem] text-white shadow-ph ring-8 ring-ph-muted">
|
|
24
|
+
Replay
|
|
25
|
+
</div>
|
|
26
|
+
<div className="absolute inset-x-5 top-0 flex h-[5.75rem] items-center justify-center rounded-xl border border-ph-border bg-ph-surface shadow-ph-md ring-8 ring-ph-muted">
|
|
27
|
+
<span className="text-sm font-bold text-ph-brand">Trends insight</span>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</ShowcaseWrapper>
|
|
32
|
+
);
|
|
33
|
+
}
|