ruler-components 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/AGENTS.md +9 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +21 -0
  4. package/README.md +45 -0
  5. package/app/docs/[slug]/page.tsx +147 -0
  6. package/app/docs/layout.tsx +26 -0
  7. package/app/docs/page.tsx +148 -0
  8. package/app/favicon.ico +0 -0
  9. package/app/globals.css +168 -0
  10. package/app/icon.png +0 -0
  11. package/app/layout.tsx +90 -0
  12. package/app/opengraph-image.tsx +10 -0
  13. package/app/page.tsx +34 -0
  14. package/app/twitter-image.tsx +10 -0
  15. package/components/docs/accordion-doc-page.tsx +352 -0
  16. package/components/docs/alert-doc-page.tsx +293 -0
  17. package/components/docs/allinput-doc-page.tsx +183 -0
  18. package/components/docs/avatar-doc-page.tsx +424 -0
  19. package/components/docs/badge-doc-page.tsx +363 -0
  20. package/components/docs/button-doc-page.tsx +406 -0
  21. package/components/docs/card-doc-page.tsx +364 -0
  22. package/components/docs/checkbox-doc-page.tsx +277 -0
  23. package/components/docs/colors-doc-page.tsx +430 -0
  24. package/components/docs/dialog-doc-page.tsx +364 -0
  25. package/components/docs/docs-footer.tsx +11 -0
  26. package/components/docs/docs-mobile-menu-bar.tsx +22 -0
  27. package/components/docs/docs-mobile-nav-context.tsx +27 -0
  28. package/components/docs/docs-page.tsx +263 -0
  29. package/components/docs/docs-search.tsx +303 -0
  30. package/components/docs/docs-sidebar.tsx +165 -0
  31. package/components/docs/docs-topbar.tsx +32 -0
  32. package/components/docs/dropdown-doc-page.tsx +522 -0
  33. package/components/docs/getting-started-doc-page.tsx +205 -0
  34. package/components/docs/label-doc-page.tsx +173 -0
  35. package/components/docs/loaders-doc-page.tsx +244 -0
  36. package/components/docs/menu-doc-page.tsx +373 -0
  37. package/components/docs/modal-doc-page.tsx +436 -0
  38. package/components/docs/next-steps.tsx +53 -0
  39. package/components/docs/pagination-doc-page.tsx +316 -0
  40. package/components/docs/progress-doc-page.tsx +387 -0
  41. package/components/docs/radio-doc-page.tsx +270 -0
  42. package/components/docs/skeleton-doc-page.tsx +230 -0
  43. package/components/docs/stepper-doc-page.tsx +309 -0
  44. package/components/docs/switch-doc-page.tsx +264 -0
  45. package/components/docs/table-doc-page.tsx +689 -0
  46. package/components/docs/tabs-doc-page.tsx +359 -0
  47. package/components/docs/theming-doc-page.tsx +243 -0
  48. package/components/docs/tooltip-doc-page.tsx +357 -0
  49. package/components/docs/usage-section.tsx +21 -0
  50. package/components/icons/discord.tsx +9 -0
  51. package/components/icons/github.tsx +9 -0
  52. package/components/icons/x.tsx +9 -0
  53. package/components/installation-section.tsx +34 -0
  54. package/components/landing/feature-highlights.tsx +105 -0
  55. package/components/landing/footer-strip.tsx +45 -0
  56. package/components/landing/hero-card.tsx +14 -0
  57. package/components/landing/hero-spotlight.tsx +126 -0
  58. package/components/landing/hero.tsx +64 -0
  59. package/components/landing/navbar.tsx +44 -0
  60. package/components/landing/quick-start-section.tsx +59 -0
  61. package/components/landing/site-footer.tsx +55 -0
  62. package/components/theme-provider.tsx +60 -0
  63. package/components/ui/accordion.tsx +174 -0
  64. package/components/ui/alert.tsx +103 -0
  65. package/components/ui/allinput.tsx +210 -0
  66. package/components/ui/avatar.tsx +179 -0
  67. package/components/ui/badge.tsx +52 -0
  68. package/components/ui/button.tsx +75 -0
  69. package/components/ui/card.tsx +105 -0
  70. package/components/ui/checkbox.tsx +130 -0
  71. package/components/ui/code-block.tsx +60 -0
  72. package/components/ui/data-table.tsx +235 -0
  73. package/components/ui/dialog.tsx +173 -0
  74. package/components/ui/dropdown.tsx +78 -0
  75. package/components/ui/feature-card.tsx +38 -0
  76. package/components/ui/input.tsx +44 -0
  77. package/components/ui/label.tsx +110 -0
  78. package/components/ui/loaders.tsx +139 -0
  79. package/components/ui/menu.tsx +118 -0
  80. package/components/ui/modal.tsx +399 -0
  81. package/components/ui/package-manager-tabs.tsx +40 -0
  82. package/components/ui/pagination.tsx +200 -0
  83. package/components/ui/progress.tsx +134 -0
  84. package/components/ui/radio.tsx +262 -0
  85. package/components/ui/skeleton.tsx +102 -0
  86. package/components/ui/steppers.tsx +309 -0
  87. package/components/ui/switch.tsx +153 -0
  88. package/components/ui/table.tsx +58 -0
  89. package/components/ui/tabs.tsx +166 -0
  90. package/components/ui/theme-toggle.tsx +60 -0
  91. package/components/ui/tooltip.tsx +238 -0
  92. package/data/color-palette.ts +35 -0
  93. package/data/docs-nav.ts +84 -0
  94. package/eslint.config.mjs +18 -0
  95. package/index.ts +8 -0
  96. package/lib/cn.ts +4 -0
  97. package/lib/color.ts +85 -0
  98. package/lib/og-image.tsx +138 -0
  99. package/lib/theme.ts +33 -0
  100. package/next.config.ts +7 -0
  101. package/package.json +30 -0
  102. package/postcss.config.mjs +7 -0
  103. package/public/dark.png +0 -0
  104. package/public/dark1.png +0 -0
  105. package/public/dark2.png +0 -0
  106. package/public/file.svg +1 -0
  107. package/public/globe.svg +1 -0
  108. package/public/light.png +0 -0
  109. package/public/light1.png +0 -0
  110. package/public/next.svg +1 -0
  111. package/public/vercel.svg +1 -0
  112. package/public/video/hero-demo.mp4 +0 -0
  113. package/public/window.svg +1 -0
  114. package/tsconfig.json +34 -0
@@ -0,0 +1,363 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import {
5
+ BadgeCheck,
6
+ Bell,
7
+ Check,
8
+ CheckCircle2,
9
+ Clock,
10
+ Copy,
11
+ Rocket,
12
+ ShieldAlert,
13
+ Star,
14
+ XCircle,
15
+ } from "lucide-react";
16
+ import { Badge, type BadgeProps } from "@/components/ui/badge";
17
+ import { Tooltip } from "@/components/ui/tooltip";
18
+ import {
19
+ DocsAccessibility,
20
+ DocsApiSection,
21
+ DocsApiTable,
22
+ DocsPage,
23
+ DocsPageHeader,
24
+ DocsSection,
25
+ DocsSectionGrid,
26
+ } from "@/components/docs/docs-page";
27
+ import { cn } from "@/lib/cn";
28
+
29
+ const API_ROWS = [
30
+ { prop: "variant", type: '"default" | "info" | "success" | "warning" | "error" | "outline"', default: '"default"', description: "The visual style variant" },
31
+ { prop: "size", type: '"sm" | "md" | "lg"', default: '"md"', description: "The size of the badge" },
32
+ { prop: "children", type: "ReactNode", default: "—", description: "The badge label content" },
33
+ { prop: "className", type: "string", default: "—", description: "Additional CSS classes" },
34
+ ];
35
+
36
+ const ACCESSIBILITY_ITEMS = [
37
+ "Use badges to supplement text, not replace it.",
38
+ "Don't rely on color alone to communicate status.",
39
+ "Keep badge labels short and descriptive.",
40
+ "Ensure sufficient color contrast for readability.",
41
+ "Avoid using badges for critical information that requires immediate action.",
42
+ ];
43
+
44
+ type Variant = NonNullable<BadgeProps["variant"]>;
45
+ type Size = NonNullable<BadgeProps["size"]>;
46
+
47
+ const VARIANTS: { value: Variant; label: string; swatch: string }[] = [
48
+ { value: "default", label: "Default", swatch: "bg-zinc-400" },
49
+ { value: "info", label: "Info", swatch: "bg-[#0070BA]" },
50
+ { value: "success", label: "Success", swatch: "bg-[#28A745]" },
51
+ { value: "warning", label: "Warning", swatch: "bg-[#E58E00]" },
52
+ { value: "error", label: "Error", swatch: "bg-[#D93025]" },
53
+ { value: "outline", label: "Outline", swatch: "bg-transparent ring-1 ring-inset ring-zinc-400" },
54
+ ];
55
+
56
+ const SIZES: Size[] = ["sm", "md", "lg"];
57
+
58
+ const MEANINGS: {
59
+ variant: Variant;
60
+ accent: string;
61
+ title: string;
62
+ guidance: string;
63
+ example: string;
64
+ }[] = [
65
+ {
66
+ variant: "default",
67
+ accent: "bg-zinc-400",
68
+ title: "Default",
69
+ guidance: "Neutral metadata — a count, a category, anything without a status.",
70
+ example: "12 items",
71
+ },
72
+ {
73
+ variant: "info",
74
+ accent: "bg-[#0070BA]",
75
+ title: "Info",
76
+ guidance: "Something is active or in motion, but no action is required yet.",
77
+ example: "In review",
78
+ },
79
+ {
80
+ variant: "success",
81
+ accent: "bg-[#28A745]",
82
+ title: "Success",
83
+ guidance: "A completed, verified, or healthy state.",
84
+ example: "Verified",
85
+ },
86
+ {
87
+ variant: "warning",
88
+ accent: "bg-[#E58E00]",
89
+ title: "Warning",
90
+ guidance: "Needs attention soon, but isn't broken yet.",
91
+ example: "Expiring soon",
92
+ },
93
+ {
94
+ variant: "error",
95
+ accent: "bg-[#D93025]",
96
+ title: "Error",
97
+ guidance: "Something failed or is blocking progress — the loudest signal in the set.",
98
+ example: "Failed",
99
+ },
100
+ {
101
+ variant: "outline",
102
+ accent: "bg-transparent ring-1 ring-inset ring-zinc-400",
103
+ title: "Outline",
104
+ guidance: "Quiet, low-emphasis tags — categories, filters, anything decorative.",
105
+ example: "Design",
106
+ },
107
+ ];
108
+
109
+ const DEPLOYS = [
110
+ { name: "marketing-site", branch: "main", status: "success" as const, label: "Deployed", time: "2m ago" },
111
+ { name: "api-gateway", branch: "feat/rate-limit", status: "info" as const, label: "Building", time: "just now" },
112
+ { name: "docs-portal", branch: "main", status: "success" as const, label: "Deployed", time: "18m ago" },
113
+ { name: "admin-dashboard", branch: "fix/auth-redirect", status: "error" as const, label: "Failed", time: "1h ago" },
114
+ { name: "billing-worker", branch: "main", status: "warning" as const, label: "Degraded", time: "3h ago" },
115
+ ];
116
+
117
+ function codeFor(variant: Variant, size: Size, withIcon: boolean) {
118
+ const props = [size !== "md" && `size="${size}"`, variant !== "default" && `variant="${variant}"`]
119
+ .filter(Boolean)
120
+ .join(" ");
121
+ const open = props ? `<Badge ${props}>` : "<Badge>";
122
+ const children = withIcon ? "<CheckCircle2 />\n Verified" : "Badge";
123
+ return `${open}\n ${children}\n</Badge>`;
124
+ }
125
+
126
+ export function BadgeDocPage() {
127
+ const [variant, setVariant] = useState<Variant>("success");
128
+ const [size, setSize] = useState<Size>("md");
129
+ const [withIcon, setWithIcon] = useState(true);
130
+ const [copied, setCopied] = useState(false);
131
+
132
+ const snippet = codeFor(variant, size, withIcon);
133
+
134
+ function copySnippet() {
135
+ navigator.clipboard.writeText(snippet).catch(() => {});
136
+ setCopied(true);
137
+ setTimeout(() => setCopied(false), 1200);
138
+ }
139
+
140
+ return (
141
+ <DocsPage>
142
+ <DocsPageHeader
143
+ icon={BadgeCheck}
144
+ title="Badge"
145
+ description="Displays a compact label for highlighting status, categories, or metadata at a glance."
146
+ />
147
+
148
+ <DocsSection
149
+ first
150
+ title="Playground"
151
+ description="Build a badge and copy the exact markup — every combination, live."
152
+ >
153
+ <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)]">
154
+ <div className="relative flex min-h-[11rem] items-center justify-center overflow-hidden bg-zinc-50 dark:bg-zinc-900/40">
155
+ <div
156
+ aria-hidden
157
+ className="pointer-events-none absolute inset-0 opacity-[0.4] dark:opacity-[0.15]"
158
+ style={{
159
+ backgroundImage:
160
+ "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)",
161
+ backgroundSize: "24px 24px",
162
+ }}
163
+ />
164
+ <div className="relative scale-[1.8]">
165
+ <Badge
166
+ key={`${variant}-${size}-${withIcon}`}
167
+ variant={variant}
168
+ size={size}
169
+ className="animate-[pagination-pop_0.25s_ease-out]"
170
+ >
171
+ {withIcon && <CheckCircle2 className={size === "sm" ? "h-2.5 w-2.5" : "h-3 w-3"} />}
172
+ {withIcon ? "Verified" : "Badge"}
173
+ </Badge>
174
+ </div>
175
+ </div>
176
+
177
+ <div className="flex flex-col gap-5 border-t border-zinc-200 p-5 dark:border-zinc-800">
178
+ <div className="flex flex-wrap items-start justify-between gap-6">
179
+ <div>
180
+ <p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
181
+ Variant
182
+ </p>
183
+ <div className="flex flex-wrap gap-1.5">
184
+ {VARIANTS.map((v) => (
185
+ <Tooltip key={v.value} content={v.label}>
186
+ <button
187
+ type="button"
188
+ onClick={() => setVariant(v.value)}
189
+ aria-label={v.label}
190
+ className={cn(
191
+ "flex h-8 w-8 items-center justify-center rounded-lg border transition-all",
192
+ variant === v.value
193
+ ? "border-zinc-900 ring-2 ring-zinc-900 ring-offset-2 ring-offset-zinc-50 dark:border-white dark:ring-white dark:ring-offset-zinc-900"
194
+ : "border-zinc-200 hover:border-zinc-300 dark:border-zinc-700 dark:hover:border-zinc-600"
195
+ )}
196
+ >
197
+ <span className={cn("h-3.5 w-3.5 rounded-full", v.swatch)} />
198
+ </button>
199
+ </Tooltip>
200
+ ))}
201
+ </div>
202
+ </div>
203
+
204
+ <div>
205
+ <p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
206
+ Size
207
+ </p>
208
+ <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">
209
+ {SIZES.map((s) => (
210
+ <button
211
+ key={s}
212
+ type="button"
213
+ onClick={() => setSize(s)}
214
+ className={cn(
215
+ "rounded-md px-3 py-1.5 text-xs font-medium uppercase tracking-wide transition-colors",
216
+ size === s
217
+ ? "bg-white text-zinc-900 shadow-sm dark:bg-zinc-800 dark:text-white"
218
+ : "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200"
219
+ )}
220
+ >
221
+ {s}
222
+ </button>
223
+ ))}
224
+ </div>
225
+ </div>
226
+
227
+ <div>
228
+ <p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
229
+ Icon
230
+ </p>
231
+ <button
232
+ type="button"
233
+ onClick={() => setWithIcon((v) => !v)}
234
+ className={cn(
235
+ "flex h-[2.125rem] items-center gap-2 rounded-lg border px-3 text-xs font-medium transition-colors",
236
+ withIcon
237
+ ? "border-zinc-900 bg-zinc-900 text-white dark:border-white dark:bg-white dark:text-zinc-900"
238
+ : "border-zinc-200 text-zinc-500 hover:border-zinc-300 dark:border-zinc-700 dark:text-zinc-400"
239
+ )}
240
+ >
241
+ <CheckCircle2 className="h-3.5 w-3.5" />
242
+ {withIcon ? "Icon on" : "Icon off"}
243
+ </button>
244
+ </div>
245
+ </div>
246
+
247
+ <div className="flex items-center justify-between gap-3 rounded-xl bg-zinc-900 px-4 py-3 dark:bg-black">
248
+ <pre className="overflow-x-auto font-mono text-[13px] leading-relaxed text-zinc-100">
249
+ <code>{snippet}</code>
250
+ </pre>
251
+ <Tooltip content={copied ? "Copied!" : "Copy code"}>
252
+ <button
253
+ type="button"
254
+ onClick={copySnippet}
255
+ 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"
256
+ >
257
+ {copied ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
258
+ </button>
259
+ </Tooltip>
260
+ </div>
261
+ </div>
262
+ </div>
263
+ </DocsSection>
264
+
265
+ <DocsSection
266
+ title="What each variant means"
267
+ description="A shared vocabulary — pick the variant that matches the state, not the one that looks nicest."
268
+ >
269
+ <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
270
+ {MEANINGS.map((m) => (
271
+ <div
272
+ key={m.variant}
273
+ className="relative overflow-hidden rounded-xl border border-zinc-200 bg-white p-4 pl-5 shadow-[0_1px_2px_rgba(0,0,0,0.03)] dark:border-zinc-800 dark:bg-zinc-950"
274
+ >
275
+ <span className={cn("absolute left-0 top-0 h-full w-1", m.accent)} aria-hidden />
276
+ <div className="flex items-center justify-between gap-2">
277
+ <p className="text-sm font-semibold text-zinc-900 dark:text-white">{m.title}</p>
278
+ <Badge variant={m.variant}>{m.example}</Badge>
279
+ </div>
280
+ <p className="mt-2 text-xs leading-relaxed text-zinc-500 dark:text-zinc-400">{m.guidance}</p>
281
+ </div>
282
+ ))}
283
+ </div>
284
+ </DocsSection>
285
+
286
+ <DocsSection title="In context" description="Badges rarely stand alone — here's how they read inside real UI.">
287
+ <DocsSectionGrid className="lg:grid-cols-[1.3fr_1fr]">
288
+ <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)]">
289
+ <div className="flex items-center justify-between border-b border-zinc-200 bg-zinc-50/70 px-4 py-2.5 dark:border-zinc-800 dark:bg-zinc-900/40">
290
+ <p className="text-sm font-semibold text-zinc-900 dark:text-white">Deployments</p>
291
+ <Badge variant="outline">{DEPLOYS.length} services</Badge>
292
+ </div>
293
+ <div className="divide-y divide-zinc-100 dark:divide-zinc-900">
294
+ {DEPLOYS.map((d) => (
295
+ <div key={d.name} className="flex items-center justify-between gap-3 px-4 py-3">
296
+ <div className="min-w-0">
297
+ <p className="truncate text-sm font-medium text-zinc-900 dark:text-white">{d.name}</p>
298
+ <p className="truncate font-mono text-xs text-zinc-400 dark:text-zinc-500">{d.branch}</p>
299
+ </div>
300
+ <div className="flex shrink-0 items-center gap-3">
301
+ <span className="text-xs text-zinc-400 dark:text-zinc-500">{d.time}</span>
302
+ <Badge variant={d.status}>{d.label}</Badge>
303
+ </div>
304
+ </div>
305
+ ))}
306
+ </div>
307
+ </div>
308
+
309
+ <div className="flex flex-col gap-4">
310
+ <div className="rounded-2xl border border-zinc-200 bg-white p-5 shadow-[0_1px_2px_rgba(0,0,0,0.03)] dark:border-zinc-800 dark:bg-zinc-950">
311
+ <div className="flex items-center gap-2">
312
+ <p className="text-sm font-semibold text-zinc-900 dark:text-white">Growth plan</p>
313
+ <Badge variant="success">
314
+ <Star className="h-3 w-3" />
315
+ Pro
316
+ </Badge>
317
+ </div>
318
+ <p className="mt-1.5 text-xs leading-relaxed text-zinc-500 dark:text-zinc-400">
319
+ Unlimited projects, priority support, and advanced analytics.
320
+ </p>
321
+ </div>
322
+
323
+ <div className="flex items-center justify-between rounded-2xl border border-zinc-200 bg-white p-5 shadow-[0_1px_2px_rgba(0,0,0,0.03)] dark:border-zinc-800 dark:bg-zinc-950">
324
+ <div className="flex items-center gap-3">
325
+ <span className="relative flex h-9 w-9 items-center justify-center rounded-full bg-zinc-100 text-zinc-500 dark:bg-zinc-900 dark:text-zinc-400">
326
+ <Bell className="h-4 w-4" />
327
+ <span className="absolute -right-1 -top-1 flex h-4 w-4 items-center justify-center rounded-full bg-[#D93025] text-[9px] font-semibold text-white ring-2 ring-white dark:ring-zinc-950">
328
+ 3
329
+ </span>
330
+ </span>
331
+ <p className="text-sm font-medium text-zinc-700 dark:text-zinc-300">Notifications</p>
332
+ </div>
333
+ <Badge variant="error">
334
+ <ShieldAlert className="h-3 w-3" />
335
+ Action needed
336
+ </Badge>
337
+ </div>
338
+
339
+ <div className="flex flex-wrap gap-2 rounded-2xl border border-zinc-200 bg-white p-5 shadow-[0_1px_2px_rgba(0,0,0,0.03)] dark:border-zinc-800 dark:bg-zinc-950">
340
+ <p className="w-full text-sm font-semibold text-zinc-900 dark:text-white">Filter by tag</p>
341
+ <Badge variant="outline">Design</Badge>
342
+ <Badge variant="outline">Engineering</Badge>
343
+ <Badge variant="outline">Marketing</Badge>
344
+ <Badge variant="outline">
345
+ <Clock className="h-3 w-3" />
346
+ Overdue
347
+ </Badge>
348
+ <Badge variant="outline">
349
+ <Rocket className="h-3 w-3" />
350
+ Launched
351
+ </Badge>
352
+ </div>
353
+ </div>
354
+ </DocsSectionGrid>
355
+ </DocsSection>
356
+
357
+ <DocsApiSection>
358
+ <DocsApiTable title="API Reference" rows={API_ROWS} />
359
+ <DocsAccessibility items={ACCESSIBILITY_ITEMS} />
360
+ </DocsApiSection>
361
+ </DocsPage>
362
+ );
363
+ }