ruler-components 0.1.0 → 0.1.2

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 (118) hide show
  1. package/dist/index.d.mts +51 -0
  2. package/dist/index.d.ts +51 -0
  3. package/dist/index.js +534 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/index.mjs +498 -0
  6. package/dist/index.mjs.map +1 -0
  7. package/package.json +21 -2
  8. package/AGENTS.md +0 -9
  9. package/CLAUDE.md +0 -1
  10. package/app/docs/[slug]/page.tsx +0 -147
  11. package/app/docs/layout.tsx +0 -26
  12. package/app/docs/page.tsx +0 -148
  13. package/app/favicon.ico +0 -0
  14. package/app/globals.css +0 -168
  15. package/app/icon.png +0 -0
  16. package/app/layout.tsx +0 -90
  17. package/app/opengraph-image.tsx +0 -10
  18. package/app/page.tsx +0 -34
  19. package/app/twitter-image.tsx +0 -10
  20. package/components/docs/accordion-doc-page.tsx +0 -352
  21. package/components/docs/alert-doc-page.tsx +0 -293
  22. package/components/docs/allinput-doc-page.tsx +0 -183
  23. package/components/docs/avatar-doc-page.tsx +0 -424
  24. package/components/docs/badge-doc-page.tsx +0 -363
  25. package/components/docs/button-doc-page.tsx +0 -406
  26. package/components/docs/card-doc-page.tsx +0 -364
  27. package/components/docs/checkbox-doc-page.tsx +0 -277
  28. package/components/docs/colors-doc-page.tsx +0 -430
  29. package/components/docs/dialog-doc-page.tsx +0 -364
  30. package/components/docs/docs-footer.tsx +0 -11
  31. package/components/docs/docs-mobile-menu-bar.tsx +0 -22
  32. package/components/docs/docs-mobile-nav-context.tsx +0 -27
  33. package/components/docs/docs-page.tsx +0 -263
  34. package/components/docs/docs-search.tsx +0 -303
  35. package/components/docs/docs-sidebar.tsx +0 -165
  36. package/components/docs/docs-topbar.tsx +0 -32
  37. package/components/docs/dropdown-doc-page.tsx +0 -522
  38. package/components/docs/getting-started-doc-page.tsx +0 -205
  39. package/components/docs/label-doc-page.tsx +0 -173
  40. package/components/docs/loaders-doc-page.tsx +0 -244
  41. package/components/docs/menu-doc-page.tsx +0 -373
  42. package/components/docs/modal-doc-page.tsx +0 -436
  43. package/components/docs/next-steps.tsx +0 -53
  44. package/components/docs/pagination-doc-page.tsx +0 -316
  45. package/components/docs/progress-doc-page.tsx +0 -387
  46. package/components/docs/radio-doc-page.tsx +0 -270
  47. package/components/docs/skeleton-doc-page.tsx +0 -230
  48. package/components/docs/stepper-doc-page.tsx +0 -309
  49. package/components/docs/switch-doc-page.tsx +0 -264
  50. package/components/docs/table-doc-page.tsx +0 -689
  51. package/components/docs/tabs-doc-page.tsx +0 -359
  52. package/components/docs/theming-doc-page.tsx +0 -243
  53. package/components/docs/tooltip-doc-page.tsx +0 -357
  54. package/components/docs/usage-section.tsx +0 -21
  55. package/components/icons/discord.tsx +0 -9
  56. package/components/icons/github.tsx +0 -9
  57. package/components/icons/x.tsx +0 -9
  58. package/components/installation-section.tsx +0 -34
  59. package/components/landing/feature-highlights.tsx +0 -105
  60. package/components/landing/footer-strip.tsx +0 -45
  61. package/components/landing/hero-card.tsx +0 -14
  62. package/components/landing/hero-spotlight.tsx +0 -126
  63. package/components/landing/hero.tsx +0 -64
  64. package/components/landing/navbar.tsx +0 -44
  65. package/components/landing/quick-start-section.tsx +0 -59
  66. package/components/landing/site-footer.tsx +0 -55
  67. package/components/theme-provider.tsx +0 -60
  68. package/components/ui/accordion.tsx +0 -174
  69. package/components/ui/alert.tsx +0 -103
  70. package/components/ui/allinput.tsx +0 -210
  71. package/components/ui/avatar.tsx +0 -179
  72. package/components/ui/badge.tsx +0 -52
  73. package/components/ui/button.tsx +0 -75
  74. package/components/ui/card.tsx +0 -105
  75. package/components/ui/checkbox.tsx +0 -130
  76. package/components/ui/code-block.tsx +0 -60
  77. package/components/ui/data-table.tsx +0 -235
  78. package/components/ui/dialog.tsx +0 -173
  79. package/components/ui/dropdown.tsx +0 -78
  80. package/components/ui/feature-card.tsx +0 -38
  81. package/components/ui/input.tsx +0 -44
  82. package/components/ui/label.tsx +0 -110
  83. package/components/ui/loaders.tsx +0 -139
  84. package/components/ui/menu.tsx +0 -118
  85. package/components/ui/modal.tsx +0 -399
  86. package/components/ui/package-manager-tabs.tsx +0 -40
  87. package/components/ui/pagination.tsx +0 -200
  88. package/components/ui/progress.tsx +0 -134
  89. package/components/ui/radio.tsx +0 -262
  90. package/components/ui/skeleton.tsx +0 -102
  91. package/components/ui/steppers.tsx +0 -309
  92. package/components/ui/switch.tsx +0 -153
  93. package/components/ui/table.tsx +0 -58
  94. package/components/ui/tabs.tsx +0 -166
  95. package/components/ui/theme-toggle.tsx +0 -60
  96. package/components/ui/tooltip.tsx +0 -238
  97. package/data/color-palette.ts +0 -35
  98. package/data/docs-nav.ts +0 -84
  99. package/eslint.config.mjs +0 -18
  100. package/index.ts +0 -8
  101. package/lib/cn.ts +0 -4
  102. package/lib/color.ts +0 -85
  103. package/lib/og-image.tsx +0 -138
  104. package/lib/theme.ts +0 -33
  105. package/next.config.ts +0 -7
  106. package/postcss.config.mjs +0 -7
  107. package/public/dark.png +0 -0
  108. package/public/dark1.png +0 -0
  109. package/public/dark2.png +0 -0
  110. package/public/file.svg +0 -1
  111. package/public/globe.svg +0 -1
  112. package/public/light.png +0 -0
  113. package/public/light1.png +0 -0
  114. package/public/next.svg +0 -1
  115. package/public/vercel.svg +0 -1
  116. package/public/video/hero-demo.mp4 +0 -0
  117. package/public/window.svg +0 -1
  118. package/tsconfig.json +0 -34
@@ -1,230 +0,0 @@
1
- "use client";
2
-
3
- import React, { useState } from "react";
4
- import { Check, Copy, Rows3 } from "lucide-react";
5
- import {
6
- AccordionSkeletonPlus,
7
- AccordionSkeletonChevron,
8
- CardSkeletonGrid,
9
- ListSkeleton,
10
- TableSkeleton
11
- } from "@/components/ui/skeleton";
12
- import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card";
13
- import { Tooltip } from "@/components/ui/tooltip";
14
- import {
15
- DocsAccessibility,
16
- DocsApiSection,
17
- DocsPage,
18
- DocsPageHeader,
19
- DocsSection,
20
- DocsSectionGrid,
21
- DocsSimpleTable,
22
- } from "@/components/docs/docs-page";
23
- import { cn } from "@/lib/cn";
24
-
25
- const COMPONENT_ROWS = [
26
- { name: "AccordionSkeletonPlus", description: "Rows matching the plus-style accordion — icon circle and title bar. Accepts count (default 3)." },
27
- { name: "AccordionSkeletonChevron", description: "Rows matching the chevron-style accordion — title bar and trailing icon. Accepts count (default 3)." },
28
- { name: "CardSkeletonGrid", description: "A responsive grid of image, title, and description placeholders. Accepts count (default 3)." },
29
- { name: "ListSkeleton", description: "A feed-style card with an avatar and multi-line text placeholders. Accepts count (default 3)." },
30
- { name: "TableSkeleton", description: "A structured data table placeholder with headers and multiple rows. Accepts count (default 5)." },
31
- ];
32
-
33
- const ACCESSIBILITY_ITEMS = [
34
- "Wrap a skeleton group in a container with aria-busy=\"true\" so assistive tech knows content is still loading.",
35
- "Skeletons are decorative — hide them from screen readers with aria-hidden if a loading announcement is provided elsewhere.",
36
- "Match each skeleton's shape and dimensions to the real content to prevent layout shift once it loads.",
37
- "Swap the skeleton for real content, or an error state, as soon as data resolves — never leave it rendered indefinitely.",
38
- "Keep the pulse animation slow and subtle — avoid flashing effects that could trigger photosensitivity.",
39
- ];
40
-
41
- type Style = "accordion-plus" | "accordion-chevron" | "card-grid" | "list" | "table";
42
-
43
- const STYLES: { value: Style; label: string; component: string }[] = [
44
- { value: "accordion-plus", label: "Accordion (plus)", component: "AccordionSkeletonPlus" },
45
- { value: "accordion-chevron", label: "Accordion (chevron)", component: "AccordionSkeletonChevron" },
46
- { value: "card-grid", label: "Card grid", component: "CardSkeletonGrid" },
47
- { value: "list", label: "List", component: "ListSkeleton" },
48
- { value: "table", label: "Table", component: "TableSkeleton" },
49
- ];
50
-
51
- const COUNTS = [2, 3, 5] as const;
52
-
53
- const STYLE_RENDERERS: Record<Style, (count: number) => React.ReactNode> = {
54
- "accordion-plus": (count) => <AccordionSkeletonPlus count={count} />,
55
- "accordion-chevron": (count) => <AccordionSkeletonChevron count={count} />,
56
- "card-grid": (count) => <CardSkeletonGrid count={count} />,
57
- list: (count) => <ListSkeleton count={count} />,
58
- table: (count) => <TableSkeleton count={count} />,
59
- };
60
-
61
- function codeFor(style: Style, count: number) {
62
- const { component } = STYLES.find((s) => s.value === style)!;
63
- return `import { ${component} } from "@/components/ui/skeleton"\n\n<${component} count={${count}} />`;
64
- }
65
-
66
- export function SkeletonDocPage() {
67
- const [style, setStyle] = useState<Style>("list");
68
- const [count, setCount] = useState(3);
69
- const [copied, setCopied] = useState(false);
70
-
71
- const snippet = codeFor(style, count);
72
-
73
- function copySnippet() {
74
- navigator.clipboard.writeText(snippet).catch(() => {});
75
- setCopied(true);
76
- setTimeout(() => setCopied(false), 1200);
77
- }
78
-
79
- return (
80
- <DocsPage>
81
- <DocsPageHeader
82
- icon={Rows3}
83
- title="Skeleton"
84
- description="Placeholder loading states that mirror the shape of real content — accordions, card grids, and feeds — to reduce layout shift and improve perceived performance."
85
- />
86
-
87
- <DocsSection
88
- first
89
- title="Playground"
90
- description="Pick a layout and item count, then copy the exact markup — live."
91
- >
92
- <div className="overflow-hidden rounded-2xl border border-zinc-200 shadow-[0_2px_8px_rgba(0,0,0,0.04)] dark:border-zinc-800 dark:shadow-[0_2px_8px_rgba(0,0,0,0.2)]">
93
- <div className="relative flex min-h-[12rem] items-center justify-center overflow-hidden bg-zinc-50 p-6 dark:bg-zinc-900/40">
94
- <div
95
- aria-hidden
96
- className="pointer-events-none absolute inset-0 opacity-[0.4] dark:opacity-[0.15]"
97
- style={{
98
- backgroundImage:
99
- "linear-gradient(to right, rgb(161 161 170 / 0.35) 1px, transparent 1px), linear-gradient(to bottom, rgb(161 161 170 / 0.35) 1px, transparent 1px)",
100
- backgroundSize: "24px 24px",
101
- }}
102
- />
103
- <div
104
- key={`${style}-${count}`}
105
- className="relative w-full max-w-md animate-[pagination-pop_0.2s_ease-out]"
106
- >
107
- {STYLE_RENDERERS[style](count)}
108
- </div>
109
- </div>
110
-
111
- <div className="flex flex-col gap-5 border-t border-zinc-200 p-5 dark:border-zinc-800">
112
- <div className="flex flex-wrap items-start justify-between gap-6">
113
- <div>
114
- <p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
115
- Style
116
- </p>
117
- <div className="flex flex-wrap gap-1.5">
118
- {STYLES.map((s) => (
119
- <button
120
- key={s.value}
121
- type="button"
122
- onClick={() => setStyle(s.value)}
123
- className={cn(
124
- "rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors",
125
- style === s.value
126
- ? "border-zinc-900 bg-zinc-900 text-white dark:border-white dark:bg-white dark:text-zinc-900"
127
- : "border-zinc-200 text-zinc-500 hover:border-zinc-300 dark:border-zinc-700 dark:text-zinc-400 dark:hover:border-zinc-600"
128
- )}
129
- >
130
- {s.label}
131
- </button>
132
- ))}
133
- </div>
134
- </div>
135
-
136
- <div>
137
- <p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
138
- Count
139
- </p>
140
- <div className="flex flex-wrap items-center gap-1 rounded-lg border border-zinc-200 bg-zinc-50 p-1 dark:border-zinc-800 dark:bg-zinc-900/60">
141
- {COUNTS.map((c) => (
142
- <button
143
- key={c}
144
- type="button"
145
- onClick={() => setCount(c)}
146
- className={cn(
147
- "rounded-md px-3 py-1.5 text-xs font-medium uppercase tracking-wide transition-colors",
148
- count === c
149
- ? "bg-white text-zinc-900 shadow-sm dark:bg-zinc-800 dark:text-white"
150
- : "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200"
151
- )}
152
- >
153
- {c}
154
- </button>
155
- ))}
156
- </div>
157
- </div>
158
- </div>
159
-
160
- <div className="flex items-center justify-between gap-3 rounded-xl bg-zinc-900 px-4 py-3 dark:bg-black">
161
- <pre className="overflow-x-auto font-mono text-[13px] leading-relaxed text-zinc-100">
162
- <code>{snippet}</code>
163
- </pre>
164
- <Tooltip content={copied ? "Copied!" : "Copy code"}>
165
- <button
166
- type="button"
167
- onClick={copySnippet}
168
- className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-zinc-400 transition-colors hover:bg-white/10 hover:text-white"
169
- >
170
- {copied ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
171
- </button>
172
- </Tooltip>
173
- </div>
174
- </div>
175
- </div>
176
- </DocsSection>
177
-
178
- <DocsSection title="Default" description="Match each skeleton to the component it's standing in for.">
179
- <Card className="max-w-md p-4">
180
- <AccordionSkeletonPlus />
181
- </Card>
182
- </DocsSection>
183
-
184
- <DocsSection title="Accordion Variants" description="Skeletons for both accordion trigger styles.">
185
- <DocsSectionGrid>
186
- <Card>
187
- <CardHeader>
188
- <CardTitle className="text-base">Plus style</CardTitle>
189
- <CardDescription>Matches the default accordion trigger.</CardDescription>
190
- </CardHeader>
191
- <CardContent>
192
- <AccordionSkeletonPlus />
193
- </CardContent>
194
- </Card>
195
- <Card>
196
- <CardHeader>
197
- <CardTitle className="text-base">Chevron style</CardTitle>
198
- <CardDescription>Matches the chevron accordion trigger.</CardDescription>
199
- </CardHeader>
200
- <CardContent>
201
- <AccordionSkeletonChevron />
202
- </CardContent>
203
- </Card>
204
- </DocsSectionGrid>
205
- </DocsSection>
206
-
207
- <DocsSection title="Card Grid" description="A media-heavy placeholder for image and text cards.">
208
- <Card className="p-6">
209
- <CardSkeletonGrid count={3} />
210
- </Card>
211
- </DocsSection>
212
-
213
- <DocsSection title="Feed / List" description="A detailed placeholder with an avatar and multiple text lines.">
214
- <div className="grid gap-4 sm:grid-cols-2">
215
- <ListSkeleton count={1} />
216
- <ListSkeleton count={1} />
217
- </div>
218
- </DocsSection>
219
-
220
- <DocsSection title="Data Table" description="A comprehensive layout for list-based data views.">
221
- <TableSkeleton count={8} />
222
- </DocsSection>
223
-
224
- <DocsApiSection>
225
- <DocsSimpleTable title="Available Skeletons" rows={COMPONENT_ROWS} />
226
- <DocsAccessibility items={ACCESSIBILITY_ITEMS} />
227
- </DocsApiSection>
228
- </DocsPage>
229
- );
230
- }
@@ -1,309 +0,0 @@
1
- "use client";
2
-
3
- import React, { useState } from "react";
4
- import { Check, ChevronLeft, ChevronRight, Copy, Layers, ShieldCheck, Box, Truck, CheckCircle2 } from "lucide-react";
5
- import {
6
- HorizontalStepper, VerticalStepper, ProgressStepper,
7
- DotStepper, SegmentedStepper, BreadcrumbStepper,
8
- TimelineStepper, InteractiveStepper, type Step
9
- } from "@/components/ui/steppers";
10
- import { Card } from "@/components/ui/card";
11
- import { Tooltip } from "@/components/ui/tooltip";
12
- import {
13
- DocsAccessibility,
14
- DocsApiSection,
15
- DocsApiTable,
16
- DocsPage,
17
- DocsPageHeader,
18
- DocsSection,
19
- DocsSectionGrid,
20
- DocsSimpleTable,
21
- DocsSubsection,
22
- } from "@/components/docs/docs-page";
23
- import { cn } from "@/lib/cn";
24
-
25
- const DEMO_STEPS: Step[] = [
26
- { title: "Verification", description: "Identity and document check", icon: ShieldCheck },
27
- { title: "Inventory", description: "Product stock allocation", icon: Box },
28
- { title: "Shipping", description: "Logistics and dispatching", icon: Truck },
29
- { title: "Delivery", description: "Final destination arrival", icon: CheckCircle2 },
30
- ];
31
-
32
- const PLAYGROUND_STEPS: Step[] = [
33
- { title: "Account" },
34
- { title: "Profile" },
35
- { title: "Payment" },
36
- { title: "Confirm" },
37
- ];
38
-
39
- type StyleKey = "horizontal" | "vertical" | "progress" | "dot" | "segmented";
40
-
41
- const STYLES: { value: StyleKey; label: string; component: React.ComponentType<{ steps: Step[]; currentStep: number; className?: string }>; importName: string }[] = [
42
- { value: "horizontal", label: "Horizontal", component: HorizontalStepper, importName: "HorizontalStepper" },
43
- { value: "vertical", label: "Vertical", component: VerticalStepper, importName: "VerticalStepper" },
44
- { value: "progress", label: "Progress", component: ProgressStepper, importName: "ProgressStepper" },
45
- { value: "dot", label: "Dot", component: DotStepper, importName: "DotStepper" },
46
- { value: "segmented", label: "Segmented", component: SegmentedStepper, importName: "SegmentedStepper" },
47
- ];
48
-
49
- function codeFor(importName: string, currentStep: number) {
50
- const stepsSrc = PLAYGROUND_STEPS.map((s) => ` { title: "${s.title}" },`).join("\n");
51
- return `import { ${importName} } from "@/components/ui/steppers";\n\nconst steps = [\n${stepsSrc}\n];\n\n<${importName} steps={steps} currentStep={${currentStep}} />`;
52
- }
53
-
54
- const API_ROWS = [
55
- { prop: "steps", type: "Step[]", default: "—", description: "Ordered list of steps, each with a title, optional description, and icon." },
56
- { prop: "currentStep", type: "number", default: "—", description: "Zero-based index of the active step." },
57
- { prop: "onStepClick", type: "(step: number) => void", default: "—", description: "Called with the clicked index. Only wired up by InteractiveStepper." },
58
- { prop: "className", type: "string", default: "—", description: "Extra classes applied to the stepper's root element." },
59
- ];
60
-
61
- const ACCESSIBILITY_ITEMS = [
62
- "Wrap the stepper in a <nav> landmark for assistive technology.",
63
- "aria-current=\"step\" is applied to the active indicator automatically.",
64
- "Interactive steps have clearly defined focus-visible rings for keyboard users.",
65
- "Provide text alternatives for any icons used within the steps.",
66
- "Contrast ratios between background and text meet 4.5:1 standards in both themes.",
67
- ];
68
-
69
- function useStepper(length: number, initial = 0) {
70
- const [step, setStep] = useState(Math.min(initial, length - 1));
71
- return {
72
- step,
73
- setStep,
74
- next: () => setStep((s) => Math.min(length - 1, s + 1)),
75
- prev: () => setStep((s) => Math.max(0, s - 1)),
76
- };
77
- }
78
-
79
- function StepperControls({
80
- step,
81
- total,
82
- onPrev,
83
- onNext,
84
- }: {
85
- step: number;
86
- total: number;
87
- onPrev: () => void;
88
- onNext: () => void;
89
- }) {
90
- return (
91
- <div className="mt-6 flex items-center justify-between border-t border-zinc-100 pt-4 dark:border-zinc-800">
92
- <button
93
- type="button"
94
- onClick={onPrev}
95
- disabled={step === 0}
96
- className="flex h-8 w-8 items-center justify-center rounded-lg border border-zinc-200 text-zinc-600 transition-colors hover:bg-zinc-50 disabled:opacity-30 disabled:hover:bg-transparent dark:border-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-800"
97
- aria-label="Previous step"
98
- >
99
- <ChevronLeft size={16} />
100
- </button>
101
- <span className="text-[11px] font-semibold uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
102
- Step {step + 1} of {total}
103
- </span>
104
- <button
105
- type="button"
106
- onClick={onNext}
107
- disabled={step === total - 1}
108
- className="flex h-8 w-8 items-center justify-center rounded-lg border border-zinc-200 text-zinc-600 transition-colors hover:bg-zinc-50 disabled:opacity-30 disabled:hover:bg-transparent dark:border-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-800"
109
- aria-label="Next step"
110
- >
111
- <ChevronRight size={16} />
112
- </button>
113
- </div>
114
- );
115
- }
116
-
117
- export function StepperDocPage() {
118
- const horizontal = useStepper(DEMO_STEPS.length, 1);
119
- const vertical = useStepper(DEMO_STEPS.length, 2);
120
- const progress = useStepper(DEMO_STEPS.length, 1);
121
- const breadcrumbSegmented = useStepper(DEMO_STEPS.length, 2);
122
- const icon = useStepper(DEMO_STEPS.length, 1);
123
- const numberedDot = useStepper(DEMO_STEPS.length, 2);
124
- const timeline = useStepper(3, 1);
125
- const interactive = useStepper(DEMO_STEPS.length, 1);
126
-
127
- const [style, setStyle] = useState<StyleKey>("horizontal");
128
- const [currentStep, setCurrentStep] = useState(1);
129
- const [copied, setCopied] = useState(false);
130
-
131
- const active = STYLES.find((s) => s.value === style) ?? STYLES[0];
132
- const ActiveStepper = active.component;
133
- const snippet = codeFor(active.importName, currentStep);
134
-
135
- function copySnippet() {
136
- navigator.clipboard.writeText(snippet).catch(() => {});
137
- setCopied(true);
138
- setTimeout(() => setCopied(false), 1200);
139
- }
140
-
141
- return (
142
- <DocsPage>
143
- <DocsPageHeader
144
- icon={Layers}
145
- title="Stepper"
146
- description="A collection of 10 stepper variants with a minimalist black & white aesthetic, a success-green accent for completed steps, and smooth Framer Motion animations. Each demo below keeps its own state — driving one never affects another."
147
- />
148
-
149
- <DocsSection
150
- first
151
- title="Playground"
152
- description="Switch between stepper styles and step through the flow — live, with the exact markup to match."
153
- >
154
- <div className="overflow-hidden rounded-2xl border border-zinc-200 shadow-[0_2px_8px_rgba(0,0,0,0.04)] dark:border-zinc-800 dark:shadow-[0_2px_8px_rgba(0,0,0,0.2)]">
155
- <div className="relative flex min-h-[12rem] items-center justify-center overflow-hidden bg-zinc-50 p-6 dark:bg-zinc-900/40">
156
- <div
157
- aria-hidden
158
- className="pointer-events-none absolute inset-0 opacity-[0.4] dark:opacity-[0.15]"
159
- style={{
160
- backgroundImage:
161
- "linear-gradient(to right, rgb(161 161 170 / 0.35) 1px, transparent 1px), linear-gradient(to bottom, rgb(161 161 170 / 0.35) 1px, transparent 1px)",
162
- backgroundSize: "24px 24px",
163
- }}
164
- />
165
- <div
166
- key={`${style}-${currentStep}`}
167
- className="relative w-full max-w-sm animate-[pagination-pop_0.2s_ease-out]"
168
- >
169
- <ActiveStepper steps={PLAYGROUND_STEPS} currentStep={currentStep} />
170
- </div>
171
- </div>
172
-
173
- <div className="flex flex-col gap-5 border-t border-zinc-200 p-5 dark:border-zinc-800">
174
- <div className="flex flex-wrap items-start justify-between gap-6">
175
- <div>
176
- <p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
177
- Style
178
- </p>
179
- <div className="flex flex-wrap items-center gap-1 rounded-lg border border-zinc-200 bg-zinc-50 p-1 dark:border-zinc-800 dark:bg-zinc-900/60">
180
- {STYLES.map((s) => (
181
- <button
182
- key={s.value}
183
- type="button"
184
- onClick={() => setStyle(s.value)}
185
- className={cn(
186
- "rounded-md px-3 py-1.5 text-xs font-medium uppercase tracking-wide transition-colors",
187
- style === s.value
188
- ? "bg-white text-zinc-900 shadow-sm dark:bg-zinc-800 dark:text-white"
189
- : "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200"
190
- )}
191
- >
192
- {s.label}
193
- </button>
194
- ))}
195
- </div>
196
- </div>
197
-
198
- <div>
199
- <p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
200
- Current step
201
- </p>
202
- <div className="flex items-center gap-2">
203
- <button
204
- type="button"
205
- onClick={() => setCurrentStep((s) => Math.max(0, s - 1))}
206
- disabled={currentStep === 0}
207
- className="flex h-8 w-8 items-center justify-center rounded-lg border border-zinc-200 text-zinc-600 transition-colors hover:bg-zinc-50 disabled:opacity-30 disabled:hover:bg-transparent dark:border-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-800"
208
- aria-label="Previous step"
209
- >
210
- <ChevronLeft size={16} />
211
- </button>
212
- <span className="w-20 text-center text-[11px] font-semibold uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
213
- {currentStep + 1} of {PLAYGROUND_STEPS.length}
214
- </span>
215
- <button
216
- type="button"
217
- onClick={() => setCurrentStep((s) => Math.min(PLAYGROUND_STEPS.length - 1, s + 1))}
218
- disabled={currentStep === PLAYGROUND_STEPS.length - 1}
219
- className="flex h-8 w-8 items-center justify-center rounded-lg border border-zinc-200 text-zinc-600 transition-colors hover:bg-zinc-50 disabled:opacity-30 disabled:hover:bg-transparent dark:border-zinc-700 dark:text-zinc-300 dark:hover:bg-zinc-800"
220
- aria-label="Next step"
221
- >
222
- <ChevronRight size={16} />
223
- </button>
224
- </div>
225
- </div>
226
- </div>
227
-
228
- <div className="flex items-center justify-between gap-3 rounded-xl bg-zinc-900 px-4 py-3 dark:bg-black">
229
- <pre className="overflow-x-auto font-mono text-[13px] leading-relaxed text-zinc-100">
230
- <code>{snippet}</code>
231
- </pre>
232
- <Tooltip content={copied ? "Copied!" : "Copy code"}>
233
- <button
234
- type="button"
235
- onClick={copySnippet}
236
- className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg text-zinc-400 transition-colors hover:bg-white/10 hover:text-white"
237
- >
238
- {copied ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
239
- </button>
240
- </Tooltip>
241
- </div>
242
- </div>
243
- </div>
244
- </DocsSection>
245
-
246
- <DocsSection title="Standard Flows" description="Common patterns for linear progression.">
247
- <DocsSectionGrid>
248
- <DocsSubsection title="Horizontal" description="A classic top-of-form progress rail.">
249
- <Card className="flex h-full flex-col justify-between p-8">
250
- <div className="flex flex-1 items-center justify-center">
251
- <HorizontalStepper steps={DEMO_STEPS} currentStep={horizontal.step} />
252
- </div>
253
- <StepperControls step={horizontal.step} total={DEMO_STEPS.length} onPrev={horizontal.prev} onNext={horizontal.next} />
254
- </Card>
255
- </DocsSubsection>
256
- <DocsSubsection title="Vertical" description="Best for detailed, multi-line steps.">
257
- <Card className="flex h-full flex-col justify-between p-8">
258
- <VerticalStepper steps={DEMO_STEPS} currentStep={vertical.step} />
259
- <StepperControls step={vertical.step} total={DEMO_STEPS.length} onPrev={vertical.prev} onNext={vertical.next} />
260
- </Card>
261
- </DocsSubsection>
262
- </DocsSectionGrid>
263
- </DocsSection>
264
-
265
- <DocsSection title="Metrics & Segments" description="Visualizing data-driven completion states.">
266
- <DocsSectionGrid>
267
- <DocsSubsection title="Progress" description="Turns green once the flow reaches 100%.">
268
- <Card className="flex h-full flex-col justify-between p-8">
269
- <div className="flex flex-1 items-center">
270
- <ProgressStepper steps={DEMO_STEPS} currentStep={progress.step} />
271
- </div>
272
- <StepperControls step={progress.step} total={DEMO_STEPS.length} onPrev={progress.prev} onNext={progress.next} />
273
- </Card>
274
- </DocsSubsection>
275
- <DocsSubsection title="Breadcrumb & Segmented" description="Compact bars for dashboards and toolbars.">
276
- <Card className="flex h-full flex-col justify-between p-8">
277
- <div className="flex flex-1 flex-col justify-center gap-8">
278
- <BreadcrumbStepper steps={DEMO_STEPS} currentStep={breadcrumbSegmented.step} />
279
- <SegmentedStepper steps={DEMO_STEPS} currentStep={breadcrumbSegmented.step} />
280
- </div>
281
- <StepperControls step={breadcrumbSegmented.step} total={DEMO_STEPS.length} onPrev={breadcrumbSegmented.prev} onNext={breadcrumbSegmented.next} />
282
- </Card>
283
- </DocsSubsection>
284
- </DocsSectionGrid>
285
- </DocsSection>
286
-
287
- <DocsSection title="Deep Navigation" description="Extended history and interactive control styles.">
288
- <DocsSectionGrid>
289
- <DocsSubsection title="Timeline" description="An activity-log style history view.">
290
- <Card className="flex h-full flex-col justify-between p-8">
291
- <TimelineStepper steps={DEMO_STEPS.slice(0, 3)} currentStep={timeline.step} />
292
- <StepperControls step={timeline.step} total={3} onPrev={timeline.prev} onNext={timeline.next} />
293
- </Card>
294
- </DocsSubsection>
295
- <DocsSubsection title="Interactive" description="Click any card to jump directly to that step.">
296
- <Card className="flex h-full items-center justify-center border-dashed border-2 bg-zinc-50/50 p-8 dark:bg-zinc-900/30">
297
- <InteractiveStepper steps={DEMO_STEPS} currentStep={interactive.step} onStepClick={interactive.setStep} />
298
- </Card>
299
- </DocsSubsection>
300
- </DocsSectionGrid>
301
- </DocsSection>
302
-
303
- <DocsApiSection>
304
- <DocsApiTable title="API Reference" rows={API_ROWS} />
305
- <DocsAccessibility items={ACCESSIBILITY_ITEMS} />
306
- </DocsApiSection>
307
- </DocsPage>
308
- );
309
- }