ruler-components 0.1.1 → 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,359 +0,0 @@
1
- "use client";
2
-
3
- import React, { useState } from "react";
4
- import { Bell, Check, Columns3, Copy, Home, Settings, Star, TrendingUp, Users } from "lucide-react";
5
- import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs";
6
- import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card";
7
- import { Badge } from "@/components/ui/badge";
8
- import { Tooltip } from "@/components/ui/tooltip";
9
- import {
10
- DocsAccessibility,
11
- DocsApiSection,
12
- DocsApiTable,
13
- DocsPage,
14
- DocsPageHeader,
15
- DocsSection,
16
- DocsSectionGrid,
17
- } from "@/components/docs/docs-page";
18
- import { cn } from "@/lib/cn";
19
-
20
- const TABS_API_ROWS = [
21
- { prop: "children", type: "ReactNode", default: "—", description: "A TabsList and one or more TabsContent panels" },
22
- { prop: "defaultValue", type: "string", default: "—", description: "Value of the tab active on mount" },
23
- {
24
- prop: "variant",
25
- type: '"underline" | "pill" | "segmented" | "bordered" | "filled" | "icon" | "vertical" | "floating" | "scrollable" | "card"',
26
- default: '"underline"',
27
- description: "Visual style shared by the list and its triggers",
28
- },
29
- { prop: "className", type: "string", default: "—", description: "Additional CSS classes for the root wrapper" },
30
- ];
31
-
32
- const LIST_API_ROWS = [
33
- { prop: "children", type: "ReactNode", default: "—", description: "One or more TabsTrigger elements" },
34
- { prop: "className", type: "string", default: "—", description: "Additional CSS classes" },
35
- ];
36
-
37
- const TRIGGER_API_ROWS = [
38
- { prop: "value", type: "string", default: "—", description: "Unique identifier matched against a TabsContent value" },
39
- { prop: "label", type: "string", default: "—", description: "Text shown on the trigger" },
40
- { prop: "icon", type: "LucideIcon", default: "—", description: "Optional leading icon" },
41
- { prop: "badge", type: "string | number", default: "—", description: "Optional badge shown after the label" },
42
- { prop: "className", type: "string", default: "—", description: "Additional CSS classes" },
43
- ];
44
-
45
- const CONTENT_API_ROWS = [
46
- { prop: "value", type: "string", default: "—", description: "Matches the TabsTrigger value that reveals this panel" },
47
- { prop: "children", type: "ReactNode", default: "—", description: "Panel content" },
48
- { prop: "className", type: "string", default: "—", description: "Additional CSS classes" },
49
- ];
50
-
51
- const ACCESSIBILITY_ITEMS = [
52
- "TabsList carries role=\"tablist\" and each TabsTrigger carries role=\"tab\" with aria-selected reflecting the active tab.",
53
- "Only the active trigger is in the tab order (tabIndex 0) — arrive with Tab, then switch panels with a click or Enter/Space.",
54
- "Each TabsContent panel carries role=\"tabpanel\" and aria-labelledby, linking it back to its trigger for screen readers.",
55
- "Icon-only or badge-heavy triggers should still include a readable label — label is always rendered, just style it visually hidden if needed.",
56
- "Prefer the scrollable variant over shrinking text when a tab list won't fit — truncated labels are hard to scan.",
57
- ];
58
-
59
- type Variant = "underline" | "pill" | "segmented" | "bordered" | "filled" | "floating";
60
-
61
- const VARIANTS: { value: Variant; label: string }[] = [
62
- { value: "underline", label: "Underline" },
63
- { value: "pill", label: "Pill" },
64
- { value: "segmented", label: "Segmented" },
65
- { value: "bordered", label: "Bordered" },
66
- { value: "filled", label: "Filled" },
67
- { value: "floating", label: "Floating" },
68
- ];
69
-
70
- function codeFor(variant: Variant) {
71
- const props = variant !== "underline" ? ` variant="${variant}"` : "";
72
- return `<Tabs defaultValue="overview"${props}>
73
- <TabsList>
74
- <TabsTrigger value="overview" label="Overview" />
75
- <TabsTrigger value="analytics" label="Analytics" />
76
- <TabsTrigger value="settings" label="Settings" />
77
- </TabsList>
78
- <TabsContent value="overview">
79
- Snapshot of activity across your workspace.
80
- </TabsContent>
81
- <TabsContent value="analytics">
82
- Traffic, conversion, and retention trends.
83
- </TabsContent>
84
- <TabsContent value="settings">
85
- Manage preferences and integrations.
86
- </TabsContent>
87
- </Tabs>`;
88
- }
89
-
90
- export function TabsDocPage() {
91
- const [variant, setVariant] = useState<Variant>("underline");
92
- const [copied, setCopied] = useState(false);
93
-
94
- const snippet = codeFor(variant);
95
-
96
- function copySnippet() {
97
- navigator.clipboard.writeText(snippet).catch(() => {});
98
- setCopied(true);
99
- setTimeout(() => setCopied(false), 1200);
100
- }
101
-
102
- return (
103
- <DocsPage>
104
- <DocsPageHeader
105
- icon={Columns3}
106
- title="Tabs"
107
- description="Organizes related content into switchable panels — from minimal underlines to segmented, floating, and vertical layouts."
108
- />
109
-
110
- <DocsSection
111
- first
112
- title="Playground"
113
- description="Every variant, live — switch tabs, change the style, and copy the exact markup."
114
- >
115
- <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)]">
116
- <div className="relative flex min-h-[12rem] items-center justify-center overflow-x-auto bg-zinc-50 p-4 dark:bg-zinc-900/40 sm:p-6">
117
- <div
118
- aria-hidden
119
- className="pointer-events-none absolute inset-0 opacity-[0.4] dark:opacity-[0.15]"
120
- style={{
121
- backgroundImage:
122
- "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)",
123
- backgroundSize: "24px 24px",
124
- }}
125
- />
126
- <div key={variant} className="relative w-full min-w-0 max-w-md animate-[pagination-pop_0.2s_ease-out]">
127
- <Tabs defaultValue="overview" variant={variant}>
128
- <TabsList>
129
- <TabsTrigger value="overview" label="Overview" />
130
- <TabsTrigger value="analytics" label="Analytics" />
131
- <TabsTrigger value="settings" label="Settings" />
132
- </TabsList>
133
- <TabsContent value="overview" className="pt-4 text-sm text-zinc-600 dark:text-zinc-300">
134
- Snapshot of activity across your workspace.
135
- </TabsContent>
136
- <TabsContent value="analytics" className="pt-4 text-sm text-zinc-600 dark:text-zinc-300">
137
- Traffic, conversion, and retention trends.
138
- </TabsContent>
139
- <TabsContent value="settings" className="pt-4 text-sm text-zinc-600 dark:text-zinc-300">
140
- Manage preferences and integrations.
141
- </TabsContent>
142
- </Tabs>
143
- </div>
144
- </div>
145
-
146
- <div className="flex flex-col gap-5 border-t border-zinc-200 p-5 dark:border-zinc-800">
147
- <div className="flex flex-wrap items-start justify-between gap-6">
148
- <div>
149
- <p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
150
- Variant
151
- </p>
152
- <div className="flex flex-wrap gap-1.5">
153
- {VARIANTS.map((v) => (
154
- <button
155
- key={v.value}
156
- type="button"
157
- onClick={() => setVariant(v.value)}
158
- className={cn(
159
- "rounded-lg border px-3 py-1.5 text-xs font-medium transition-colors",
160
- variant === v.value
161
- ? "border-zinc-900 bg-zinc-900 text-white dark:border-white dark:bg-white dark:text-zinc-900"
162
- : "border-zinc-200 text-zinc-500 hover:border-zinc-300 hover:text-zinc-800 dark:border-zinc-700 dark:text-zinc-400 dark:hover:border-zinc-600 dark:hover:text-zinc-200"
163
- )}
164
- >
165
- {v.label}
166
- </button>
167
- ))}
168
- </div>
169
- </div>
170
- </div>
171
-
172
- <div className="flex items-center justify-between gap-3 rounded-xl bg-zinc-900 px-4 py-3 dark:bg-black">
173
- <pre className="overflow-x-auto font-mono text-[13px] leading-relaxed text-zinc-100">
174
- <code>{snippet}</code>
175
- </pre>
176
- <Tooltip content={copied ? "Copied!" : "Copy code"}>
177
- <button
178
- type="button"
179
- onClick={copySnippet}
180
- 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"
181
- >
182
- {copied ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
183
- </button>
184
- </Tooltip>
185
- </div>
186
- </div>
187
- </div>
188
- </DocsSection>
189
-
190
- <DocsSection title="Default" description="Underline tabs switch between panels without leaving the page.">
191
- <Card className="max-w-2xl">
192
- <CardHeader className="border-b border-zinc-100 dark:border-zinc-800">
193
- <div className="flex items-center justify-between gap-4">
194
- <div>
195
- <CardTitle>Project overview</CardTitle>
196
- <CardDescription>Track progress, activity, and settings in one place.</CardDescription>
197
- </div>
198
- <Badge variant="outline">6 members</Badge>
199
- </div>
200
- </CardHeader>
201
- <CardContent className="pt-5">
202
- <Tabs defaultValue="overview">
203
- <TabsList>
204
- <TabsTrigger value="overview" label="Overview" />
205
- <TabsTrigger value="activity" label="Activity" badge={3} />
206
- <TabsTrigger value="settings" label="Settings" />
207
- </TabsList>
208
- <TabsContent value="overview" className="pt-4 text-sm text-zinc-600 dark:text-zinc-300">
209
- 12 of 20 tasks complete this sprint. The team is on track to ship by Friday.
210
- </TabsContent>
211
- <TabsContent value="activity" className="pt-4 text-sm text-zinc-600 dark:text-zinc-300">
212
- 3 new comments and 2 status changes since you last checked in.
213
- </TabsContent>
214
- <TabsContent value="settings" className="pt-4 text-sm text-zinc-600 dark:text-zinc-300">
215
- Manage notifications, integrations, and who can access this project.
216
- </TabsContent>
217
- </Tabs>
218
- </CardContent>
219
- </Card>
220
- </DocsSection>
221
-
222
- <DocsSection title="Variants" description="Pill and segmented styles suit compact filters and toggles.">
223
- <DocsSectionGrid>
224
- <Card>
225
- <CardHeader>
226
- <CardTitle className="text-base">Pill</CardTitle>
227
- <CardDescription>Rounded, high-contrast triggers for a feed or inbox filter.</CardDescription>
228
- </CardHeader>
229
- <CardContent>
230
- <Tabs defaultValue="home" variant="pill">
231
- <TabsList>
232
- <TabsTrigger value="home" label="Home" />
233
- <TabsTrigger value="following" label="Following" badge={2} />
234
- </TabsList>
235
- </Tabs>
236
- </CardContent>
237
- </Card>
238
-
239
- <Card>
240
- <CardHeader>
241
- <CardTitle className="text-base">Segmented</CardTitle>
242
- <CardDescription>An iOS-style control for switching between short, related options.</CardDescription>
243
- </CardHeader>
244
- <CardContent>
245
- <Tabs defaultValue="day" variant="segmented">
246
- <TabsList>
247
- <TabsTrigger value="day" label="Day" />
248
- <TabsTrigger value="week" label="Week" />
249
- <TabsTrigger value="month" label="Month" />
250
- </TabsList>
251
- </Tabs>
252
- </CardContent>
253
- </Card>
254
- </DocsSectionGrid>
255
- </DocsSection>
256
-
257
- <DocsSection title="Elevated & Bordered" description="A floating surface for prominent placement, or a quiet outline for secondary controls.">
258
- <DocsSectionGrid>
259
- <Card>
260
- <CardHeader>
261
- <CardTitle className="text-base">Floating</CardTitle>
262
- <CardDescription>Sits above the page on its own elevated surface.</CardDescription>
263
- </CardHeader>
264
- <CardContent>
265
- <Tabs defaultValue="all" variant="floating">
266
- <TabsList>
267
- <TabsTrigger value="all" label="All" />
268
- <TabsTrigger value="featured" label="Featured" icon={Star} />
269
- </TabsList>
270
- </Tabs>
271
- </CardContent>
272
- </Card>
273
-
274
- <Card>
275
- <CardHeader>
276
- <CardTitle className="text-base">Bordered</CardTitle>
277
- <CardDescription>A visible outline for triggers that need clear boundaries.</CardDescription>
278
- </CardHeader>
279
- <CardContent>
280
- <Tabs defaultValue="a" variant="bordered">
281
- <TabsList>
282
- <TabsTrigger value="a" label="Analytical" />
283
- <TabsTrigger value="b" label="Behavioral" />
284
- </TabsList>
285
- </Tabs>
286
- </CardContent>
287
- </Card>
288
- </DocsSectionGrid>
289
- </DocsSection>
290
-
291
- <DocsSection title="Layouts" description="Vertical tabs for settings sidebars, icon tabs for compact dashboards.">
292
- <DocsSectionGrid>
293
- <Card>
294
- <CardHeader>
295
- <CardTitle className="text-base">Vertical</CardTitle>
296
- <CardDescription>Ideal for a settings page with several sections.</CardDescription>
297
- </CardHeader>
298
- <CardContent>
299
- <Tabs defaultValue="profile" variant="vertical">
300
- <TabsList>
301
- <TabsTrigger value="profile" label="Profile" icon={Home} />
302
- <TabsTrigger value="security" label="Security" icon={Settings} />
303
- <TabsTrigger value="notifications" label="Notifications" icon={Bell} />
304
- </TabsList>
305
- <TabsContent value="profile" className="text-sm text-zinc-600 dark:text-zinc-300">
306
- Manage your name, photo, and public profile details.
307
- </TabsContent>
308
- <TabsContent value="security" className="text-sm text-zinc-600 dark:text-zinc-300">
309
- Update your password and two-factor authentication.
310
- </TabsContent>
311
- <TabsContent value="notifications" className="text-sm text-zinc-600 dark:text-zinc-300">
312
- Choose what you're notified about, and where.
313
- </TabsContent>
314
- </Tabs>
315
- </CardContent>
316
- </Card>
317
-
318
- <Card>
319
- <CardHeader>
320
- <CardTitle className="text-base">Icon</CardTitle>
321
- <CardDescription>Compact, icon-first triggers for dashboards and bottom navigation.</CardDescription>
322
- </CardHeader>
323
- <CardContent>
324
- <Tabs defaultValue="feed" variant="icon">
325
- <TabsList>
326
- <TabsTrigger value="feed" label="Feed" icon={TrendingUp} />
327
- <TabsTrigger value="friends" label="Friends" icon={Users} badge="!" />
328
- <TabsTrigger value="favs" label="Favs" icon={Star} />
329
- </TabsList>
330
- </Tabs>
331
- </CardContent>
332
- </Card>
333
- </DocsSectionGrid>
334
- </DocsSection>
335
-
336
- <DocsSection title="Scrollable" description="Overflows horizontally instead of wrapping — ideal for narrow or mobile viewports.">
337
- <Card className="max-w-md">
338
- <CardContent className="pt-6">
339
- <Tabs defaultValue="Analytics" variant="scrollable">
340
- <TabsList>
341
- {["Analytics", "Operations", "Inventory", "Logistics", "Marketing", "Sales"].map((t) => (
342
- <TabsTrigger key={t} value={t} label={t} />
343
- ))}
344
- </TabsList>
345
- </Tabs>
346
- </CardContent>
347
- </Card>
348
- </DocsSection>
349
-
350
- <DocsApiSection>
351
- <DocsApiTable title="API Reference — Tabs" rows={TABS_API_ROWS} />
352
- <DocsApiTable title="API Reference — TabsList" rows={LIST_API_ROWS} />
353
- <DocsApiTable title="API Reference — TabsTrigger" rows={TRIGGER_API_ROWS} />
354
- <DocsApiTable title="API Reference — TabsContent" rows={CONTENT_API_ROWS} />
355
- <DocsAccessibility items={ACCESSIBILITY_ITEMS} />
356
- </DocsApiSection>
357
- </DocsPage>
358
- );
359
- }
@@ -1,243 +0,0 @@
1
- "use client";
2
-
3
- import { useState } from "react";
4
- import Link from "next/link";
5
- import { ArrowRight, Check, Copy, Palette } from "lucide-react";
6
- import { Button } from "@/components/ui/button";
7
- import { Badge } from "@/components/ui/badge";
8
- import { Tooltip } from "@/components/ui/tooltip";
9
- import { DocsPage, DocsPageHeader, DocsSection, DocsSectionGrid } from "@/components/docs/docs-page";
10
- import { COLOR_PALETTE } from "@/data/color-palette";
11
- import { cn } from "@/lib/cn";
12
-
13
- const TOKENS = [
14
- { name: "--background", light: "#ffffff", dark: "#09090b", usage: "Page background" },
15
- { name: "--foreground", light: "#09090b", dark: "#fafafa", usage: "Default text color" },
16
- ];
17
-
18
- const FONTS: { token: string; family: string; sample: string; usage: string; className: string }[] = [
19
- { token: "font-sans", family: "Poppins", sample: "The quick brown fox jumps.", usage: "Body copy & UI text", className: "font-sans" },
20
- { token: "font-mono", family: "Geist Mono", sample: "const theme = \"dark\";", usage: "Code & data", className: "font-mono" },
21
- { token: "font-brand", family: "Orbitron", sample: "ruler.ui", usage: "Wordmark & headings", className: "font-brand" },
22
- ];
23
-
24
- const SNIPPET = `:root {
25
- --background: #ffffff;
26
- --foreground: #09090b;
27
- }
28
-
29
- .dark {
30
- --background: #09090b;
31
- --foreground: #fafafa;
32
- }`;
33
-
34
- function ThemeSample({ mode }: { mode: "light" | "dark" }) {
35
- const isDark = mode === "dark";
36
- return (
37
- <div
38
- className={cn(
39
- isDark && "dark",
40
- "flex flex-col gap-4 rounded-2xl border p-5",
41
- isDark ? "border-zinc-800 bg-zinc-950" : "border-zinc-200 bg-white"
42
- )}
43
- >
44
- <div className="flex items-center justify-between">
45
- <span className={cn("text-xs font-semibold uppercase tracking-wide", isDark ? "text-zinc-500" : "text-zinc-400")}>
46
- {mode}
47
- </span>
48
- <Badge variant="success">
49
- <Check className="h-3 w-3" />
50
- Active
51
- </Badge>
52
- </div>
53
- <div>
54
- <p className={cn("text-sm font-semibold", isDark ? "text-white" : "text-zinc-900")}>Workspace settings</p>
55
- <p className={cn("mt-1 text-xs leading-relaxed", isDark ? "text-zinc-400" : "text-zinc-500")}>
56
- Same components, same markup — only the CSS variables change.
57
- </p>
58
- </div>
59
- <div className="flex items-center gap-2">
60
- <Button size="sm">Save changes</Button>
61
- <Button variant="outline" size="sm">
62
- Cancel
63
- </Button>
64
- </div>
65
- </div>
66
- );
67
- }
68
-
69
- export function ThemingDocPage() {
70
- const [copied, setCopied] = useState<string | null>(null);
71
-
72
- function copy(value: string) {
73
- navigator.clipboard.writeText(value).catch(() => {});
74
- setCopied(value);
75
- setTimeout(() => setCopied((prev) => (prev === value ? null : prev)), 1200);
76
- }
77
-
78
- return (
79
- <DocsPage>
80
- <DocsPageHeader
81
- icon={Palette}
82
- title="Theming"
83
- description="Every component reads from two CSS variables and a single .dark class — no theme provider, no runtime cost."
84
- />
85
-
86
- <DocsSection
87
- first
88
- title="Light & dark, side by side"
89
- description="Both panels below render the exact same JSX. The only difference is a .dark class on the wrapper."
90
- >
91
- <div className="grid gap-4 sm:grid-cols-2">
92
- <ThemeSample mode="light" />
93
- <ThemeSample mode="dark" />
94
- </div>
95
- </DocsSection>
96
-
97
- <DocsSection
98
- title="Design tokens"
99
- description="Two variables drive the entire surface — everything else is computed from them via Tailwind's dark: variant."
100
- >
101
- <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)]">
102
- <table className="w-full text-left text-sm">
103
- <thead>
104
- <tr className="border-b border-zinc-200 bg-zinc-50/70 dark:border-zinc-800 dark:bg-zinc-900/40">
105
- <th className="px-4 py-2.5 font-medium text-zinc-500 dark:text-zinc-400">Token</th>
106
- <th className="px-4 py-2.5 font-medium text-zinc-500 dark:text-zinc-400">Light</th>
107
- <th className="px-4 py-2.5 font-medium text-zinc-500 dark:text-zinc-400">Dark</th>
108
- <th className="px-4 py-2.5 font-medium text-zinc-500 dark:text-zinc-400">Used for</th>
109
- </tr>
110
- </thead>
111
- <tbody>
112
- {TOKENS.map((token) => (
113
- <tr
114
- key={token.name}
115
- className="border-b border-zinc-200 last:border-0 dark:border-zinc-800"
116
- >
117
- <td className="px-4 py-3">
118
- <Tooltip content={copied === token.name ? "Copied!" : "Copy variable name"}>
119
- <button
120
- type="button"
121
- onClick={() => copy(token.name)}
122
- className="group flex items-center gap-2 rounded-md font-mono text-xs text-zinc-700 transition-colors hover:text-zinc-900 dark:text-zinc-300 dark:hover:text-white"
123
- >
124
- {token.name}
125
- {copied === token.name ? (
126
- <Check className="h-3 w-3 text-green-500" />
127
- ) : (
128
- <Copy className="h-3 w-3 opacity-0 transition-opacity group-hover:opacity-100" />
129
- )}
130
- </button>
131
- </Tooltip>
132
- </td>
133
- <td className="px-4 py-3">
134
- <span className="flex items-center gap-2">
135
- <span
136
- className="h-4 w-4 shrink-0 rounded-full border border-black/10"
137
- style={{ backgroundColor: token.light }}
138
- />
139
- <span className="font-mono text-xs text-zinc-500 dark:text-zinc-400">{token.light}</span>
140
- </span>
141
- </td>
142
- <td className="px-4 py-3">
143
- <span className="flex items-center gap-2">
144
- <span
145
- className="h-4 w-4 shrink-0 rounded-full border border-white/10"
146
- style={{ backgroundColor: token.dark }}
147
- />
148
- <span className="font-mono text-xs text-zinc-500 dark:text-zinc-400">{token.dark}</span>
149
- </span>
150
- </td>
151
- <td className="px-4 py-3 text-zinc-600 dark:text-zinc-300">{token.usage}</td>
152
- </tr>
153
- ))}
154
- </tbody>
155
- </table>
156
- </div>
157
- </DocsSection>
158
-
159
- <DocsSection title="Typography" description="Three font tokens, each mapped to a Tailwind utility class.">
160
- <DocsSectionGrid className="sm:grid-cols-3">
161
- {FONTS.map((font) => (
162
- <div
163
- key={font.token}
164
- className="flex flex-col justify-between gap-4 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"
165
- >
166
- <div>
167
- <p className="text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
168
- {font.family}
169
- </p>
170
- <p className={cn("mt-2 text-xl text-zinc-900 dark:text-white", font.className)}>{font.sample}</p>
171
- </div>
172
- <div className="flex items-center justify-between">
173
- <span className="text-xs text-zinc-500 dark:text-zinc-400">{font.usage}</span>
174
- <Tooltip content={copied === font.token ? "Copied!" : "Copy class name"}>
175
- <button
176
- type="button"
177
- onClick={() => copy(font.token)}
178
- className="group flex items-center gap-1.5 rounded-md font-mono text-xs text-zinc-500 transition-colors hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-white"
179
- >
180
- {font.token}
181
- {copied === font.token ? (
182
- <Check className="h-3 w-3 text-green-500" />
183
- ) : (
184
- <Copy className="h-3 w-3 opacity-0 transition-opacity group-hover:opacity-100" />
185
- )}
186
- </button>
187
- </Tooltip>
188
- </div>
189
- </div>
190
- ))}
191
- </DocsSectionGrid>
192
- </DocsSection>
193
-
194
- <DocsSection title="Color palette" description="Every hue and shade lives on its own page, with contrast built in.">
195
- <Link
196
- href="/docs/colors"
197
- className="group flex items-center justify-between gap-4 overflow-hidden rounded-2xl border border-zinc-200 bg-white p-5 shadow-[0_1px_2px_rgba(0,0,0,0.03)] transition-colors hover:border-zinc-300 dark:border-zinc-800 dark:bg-zinc-950 dark:hover:border-zinc-700"
198
- >
199
- <div className="flex items-center gap-4">
200
- <div className="flex h-11 -space-x-2">
201
- {COLOR_PALETTE.slice(0, 6).map((hue) => (
202
- <span
203
- key={hue.name}
204
- className="h-11 w-11 shrink-0 rounded-full ring-2 ring-white dark:ring-zinc-950"
205
- style={{ backgroundColor: hue.shades[5].hex }}
206
- />
207
- ))}
208
- </div>
209
- <div>
210
- <p className="text-sm font-semibold text-zinc-900 dark:text-white">
211
- {COLOR_PALETTE.length} hues · {COLOR_PALETTE.length * 11} shades
212
- </p>
213
- <p className="text-xs text-zinc-500 dark:text-zinc-400">
214
- HEX, RGB, HSL, and CSS — copy any value.
215
- </p>
216
- </div>
217
- </div>
218
- <ArrowRight className="h-4 w-4 shrink-0 text-zinc-400 transition-transform group-hover:translate-x-1 dark:text-zinc-500" />
219
- </Link>
220
- </DocsSection>
221
-
222
- <DocsSection
223
- title="Bring your own brand"
224
- description="Override the two root variables anywhere above ruler.ui's components and everything repaints — no rebuild, no config."
225
- >
226
- <div className="flex items-center justify-between gap-3 rounded-xl bg-zinc-900 px-4 py-3 dark:bg-black">
227
- <pre className="overflow-x-auto font-mono text-[13px] leading-relaxed text-zinc-100">
228
- <code>{SNIPPET}</code>
229
- </pre>
230
- <Tooltip content={copied === SNIPPET ? "Copied!" : "Copy CSS"}>
231
- <button
232
- type="button"
233
- onClick={() => copy(SNIPPET)}
234
- 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"
235
- >
236
- {copied === SNIPPET ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
237
- </button>
238
- </Tooltip>
239
- </div>
240
- </DocsSection>
241
- </DocsPage>
242
- );
243
- }