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,357 @@
1
+ "use client";
2
+
3
+ import React, { useState } from "react";
4
+ import {
5
+ ArrowUpRight,
6
+ Check,
7
+ Copy,
8
+ HelpCircle,
9
+ Info,
10
+ Link2,
11
+ MessageCircle,
12
+ MoreHorizontal,
13
+ Pencil,
14
+ Settings,
15
+ Share2,
16
+ Trash2,
17
+ } from "lucide-react";
18
+ import { Tooltip } from "@/components/ui/tooltip";
19
+ import { Button } from "@/components/ui/button";
20
+ import { Badge } from "@/components/ui/badge";
21
+ import {
22
+ Card,
23
+ CardContent,
24
+ CardDescription,
25
+ CardHeader,
26
+ CardTitle,
27
+ } from "@/components/ui/card";
28
+ import {
29
+ DocsAccessibility,
30
+ DocsApiSection,
31
+ DocsApiTable,
32
+ DocsDemoPanel,
33
+ DocsPage,
34
+ DocsPageHeader,
35
+ DocsSection,
36
+ DocsSectionGrid,
37
+ } from "@/components/docs/docs-page";
38
+ import { cn } from "@/lib/cn";
39
+
40
+ const API_ROWS = [
41
+ { prop: "content", type: "ReactNode", default: "—", description: "Tooltip content" },
42
+ { prop: "children", type: "ReactNode", default: "—", description: "Trigger element" },
43
+ { prop: "side", type: '"top" | "bottom" | "left" | "right"', default: '"top"', description: "Preferred side relative to trigger" },
44
+ { prop: "align", type: '"start" | "center" | "end"', default: '"center"', description: "Alignment along the side axis" },
45
+ { prop: "sideOffset", type: "number", default: "8", description: "Distance from the trigger in pixels" },
46
+ { prop: "delayDuration", type: "number", default: "250", description: "Delay before showing (ms)" },
47
+ { prop: "disabled", type: "boolean", default: "false", description: "Disables the tooltip" },
48
+ ];
49
+
50
+ const ACCESSIBILITY_ITEMS = [
51
+ "Appears on hover and keyboard focus.",
52
+ "Uses role=\"tooltip\" and aria-describedby.",
53
+ "Keep content short and supplementary.",
54
+ "Don't hide essential information in tooltips only.",
55
+ "Tooltip position adjusts to stay within the viewport.",
56
+ ];
57
+
58
+ type Side = "top" | "bottom" | "left" | "right";
59
+ type Align = "start" | "center" | "end";
60
+
61
+ const SIDES: Side[] = ["top", "bottom", "left", "right"];
62
+ const ALIGNS: Align[] = ["start", "center", "end"];
63
+
64
+ function codeFor(side: Side, align: Align) {
65
+ const props = [side !== "top" && `side="${side}"`, align !== "center" && `align="${align}"`]
66
+ .filter(Boolean)
67
+ .join(" ");
68
+ const open = props ? `<Tooltip content="Hint text" ${props}>` : `<Tooltip content="Hint text">`;
69
+ return `${open}\n <Button>Hover me</Button>\n</Tooltip>`;
70
+ }
71
+
72
+ export function TooltipDocPage() {
73
+ const [side, setSide] = useState<Side>("top");
74
+ const [align, setAlign] = useState<Align>("center");
75
+ const [copied, setCopied] = useState(false);
76
+
77
+ const snippet = codeFor(side, align);
78
+
79
+ function copySnippet() {
80
+ navigator.clipboard.writeText(snippet).catch(() => {});
81
+ setCopied(true);
82
+ setTimeout(() => setCopied(false), 1200);
83
+ }
84
+
85
+ return (
86
+ <DocsPage>
87
+ <DocsPageHeader
88
+ icon={MessageCircle}
89
+ title="Tooltip"
90
+ description="Displays contextual hints on hover or focus — perfect for icon buttons, truncated text, and helper labels."
91
+ />
92
+
93
+ <DocsSection
94
+ first
95
+ title="Playground"
96
+ description="Hover the trigger to preview a tooltip on any side and alignment — copy the exact markup to match."
97
+ >
98
+ <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)]">
99
+ <div className="relative flex min-h-[14rem] items-center justify-center overflow-visible bg-zinc-50 p-6 dark:bg-zinc-900/40">
100
+ <div
101
+ aria-hidden
102
+ className="pointer-events-none absolute inset-0 opacity-[0.4] dark:opacity-[0.15]"
103
+ style={{
104
+ backgroundImage:
105
+ "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)",
106
+ backgroundSize: "24px 24px",
107
+ }}
108
+ />
109
+ <div className="relative">
110
+ <Tooltip key={`${side}-${align}`} content="Hint text" side={side} align={align}>
111
+ <Button>Hover me</Button>
112
+ </Tooltip>
113
+ </div>
114
+ </div>
115
+
116
+ <div className="flex flex-col gap-5 border-t border-zinc-200 p-5 dark:border-zinc-800">
117
+ <div className="flex flex-wrap items-start justify-between gap-6">
118
+ <div>
119
+ <p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
120
+ Side
121
+ </p>
122
+ <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">
123
+ {SIDES.map((s) => (
124
+ <button
125
+ key={s}
126
+ type="button"
127
+ onClick={() => setSide(s)}
128
+ className={cn(
129
+ "rounded-md px-3 py-1.5 text-xs font-medium capitalize transition-colors",
130
+ side === s
131
+ ? "bg-white text-zinc-900 shadow-sm dark:bg-zinc-800 dark:text-white"
132
+ : "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200"
133
+ )}
134
+ >
135
+ {s}
136
+ </button>
137
+ ))}
138
+ </div>
139
+ </div>
140
+
141
+ <div>
142
+ <p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
143
+ Align
144
+ </p>
145
+ <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">
146
+ {ALIGNS.map((a) => (
147
+ <button
148
+ key={a}
149
+ type="button"
150
+ onClick={() => setAlign(a)}
151
+ className={cn(
152
+ "rounded-md px-3 py-1.5 text-xs font-medium capitalize transition-colors",
153
+ align === a
154
+ ? "bg-white text-zinc-900 shadow-sm dark:bg-zinc-800 dark:text-white"
155
+ : "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200"
156
+ )}
157
+ >
158
+ {a}
159
+ </button>
160
+ ))}
161
+ </div>
162
+ </div>
163
+ </div>
164
+
165
+ <div className="flex items-center justify-between gap-3 rounded-xl bg-zinc-900 px-4 py-3 dark:bg-black">
166
+ <pre className="overflow-x-auto font-mono text-[13px] leading-relaxed text-zinc-100">
167
+ <code>{snippet}</code>
168
+ </pre>
169
+ <Tooltip content={copied ? "Copied!" : "Copy code"}>
170
+ <button
171
+ type="button"
172
+ onClick={copySnippet}
173
+ 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"
174
+ >
175
+ {copied ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
176
+ </button>
177
+ </Tooltip>
178
+ </div>
179
+ </div>
180
+ </div>
181
+ </DocsSection>
182
+
183
+ <DocsSection
184
+ title="Default"
185
+ description="Hover or focus any control below to see the tooltip."
186
+ >
187
+ <Card className="max-w-2xl">
188
+ <CardHeader className="border-b border-zinc-100 dark:border-zinc-800">
189
+ <div className="flex items-center justify-between gap-4">
190
+ <div>
191
+ <CardTitle>Share document</CardTitle>
192
+ <CardDescription>Invite teammates or copy a link.</CardDescription>
193
+ </div>
194
+ <Badge variant="outline">Live preview</Badge>
195
+ </div>
196
+ </CardHeader>
197
+ <CardContent className="flex flex-wrap items-center gap-2 pt-6">
198
+ <Tooltip content="Copy link to clipboard">
199
+ <Button size="sm" variant="ghost">
200
+ <Link2 className="h-4 w-4" />
201
+ Copy link
202
+ </Button>
203
+ </Tooltip>
204
+ <Tooltip content="Share with your team">
205
+ <Button size="sm" variant="ghost">
206
+ <Share2 className="h-4 w-4" />
207
+ Share
208
+ </Button>
209
+ </Tooltip>
210
+ <Tooltip content="Open in new tab">
211
+ <Button size="sm" variant="ghost">
212
+ <ArrowUpRight className="h-4 w-4" />
213
+ Open
214
+ </Button>
215
+ </Tooltip>
216
+ <div className="mx-1 h-5 w-px bg-zinc-200 dark:bg-zinc-700" />
217
+ <Tooltip content="Copy to clipboard">
218
+ <Button size="sm">
219
+ <Copy className="h-4 w-4" />
220
+ Copy
221
+ </Button>
222
+ </Tooltip>
223
+ </CardContent>
224
+ </Card>
225
+ </DocsSection>
226
+
227
+ <DocsSection
228
+ title="Positions"
229
+ description="Place tooltips on any side of the trigger."
230
+ >
231
+ <DocsDemoPanel className="!py-0">
232
+ <div className="relative flex min-h-[280px] w-full items-center justify-center py-16">
233
+ <div className="flex flex-col items-center gap-8">
234
+ <Tooltip content="Appears above" side="top">
235
+ <Button variant="ghost" size="sm" className="min-w-[5.5rem] bg-white shadow-sm dark:bg-zinc-900">
236
+ Top
237
+ </Button>
238
+ </Tooltip>
239
+
240
+ <div className="flex items-center gap-8">
241
+ <Tooltip content="Appears to the left" side="left">
242
+ <Button variant="ghost" size="sm" className="min-w-[5.5rem] bg-white shadow-sm dark:bg-zinc-900">
243
+ Left
244
+ </Button>
245
+ </Tooltip>
246
+
247
+ <div className="flex h-16 w-16 items-center justify-center rounded-2xl border border-zinc-200 bg-white text-xs font-medium text-zinc-500 shadow-[0_2px_8px_rgba(0,0,0,0.04)] dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-400 dark:shadow-[0_2px_8px_rgba(0,0,0,0.2)]">
248
+ Hover
249
+ </div>
250
+
251
+ <Tooltip content="Appears to the right" side="right">
252
+ <Button variant="ghost" size="sm" className="min-w-[5.5rem] bg-white shadow-sm dark:bg-zinc-900">
253
+ Right
254
+ </Button>
255
+ </Tooltip>
256
+ </div>
257
+
258
+ <Tooltip content="Appears below" side="bottom">
259
+ <Button variant="ghost" size="sm" className="min-w-[5.5rem] bg-white shadow-sm dark:bg-zinc-900">
260
+ Bottom
261
+ </Button>
262
+ </Tooltip>
263
+ </div>
264
+ </div>
265
+ </DocsDemoPanel>
266
+ </DocsSection>
267
+
268
+ <DocsSection>
269
+ <DocsSectionGrid className="lg:grid-cols-2">
270
+ <Card>
271
+ <CardHeader>
272
+ <CardTitle className="text-base">Editor Toolbar</CardTitle>
273
+ <CardDescription>Icon-only actions with accessible labels.</CardDescription>
274
+ </CardHeader>
275
+ <CardContent>
276
+ <div className="inline-flex items-center gap-0.5 rounded-xl border border-zinc-200 bg-white p-1 shadow-sm dark:border-zinc-700 dark:bg-zinc-900">
277
+ <Tooltip content="Edit" side="bottom">
278
+ <Button variant="ghost" size="sm" aria-label="Edit" className="h-8 w-8 p-0">
279
+ <Pencil className="h-4 w-4" />
280
+ </Button>
281
+ </Tooltip>
282
+ <Tooltip content="Settings" side="bottom">
283
+ <Button variant="ghost" size="sm" aria-label="Settings" className="h-8 w-8 p-0">
284
+ <Settings className="h-4 w-4" />
285
+ </Button>
286
+ </Tooltip>
287
+ <Tooltip content="More options" side="bottom">
288
+ <Button variant="ghost" size="sm" aria-label="More options" className="h-8 w-8 p-0">
289
+ <MoreHorizontal className="h-4 w-4" />
290
+ </Button>
291
+ </Tooltip>
292
+ <div className="mx-0.5 h-5 w-px bg-zinc-200 dark:bg-zinc-700" />
293
+ <Tooltip content="Delete" side="bottom">
294
+ <Button
295
+ variant="ghost"
296
+ size="sm"
297
+ aria-label="Delete"
298
+ className="h-8 w-8 p-0 text-red-500 hover:text-red-600 dark:text-red-400"
299
+ >
300
+ <Trash2 className="h-4 w-4" />
301
+ </Button>
302
+ </Tooltip>
303
+ </div>
304
+ </CardContent>
305
+ </Card>
306
+
307
+ <Card>
308
+ <CardHeader>
309
+ <CardTitle className="text-base">Helper &amp; Status</CardTitle>
310
+ <CardDescription>Supplementary context without cluttering the UI.</CardDescription>
311
+ </CardHeader>
312
+ <CardContent className="space-y-4">
313
+ <div className="flex items-center gap-2">
314
+ <span className="text-sm font-medium text-zinc-700 dark:text-zinc-200">API Rate limit</span>
315
+ <Tooltip content="100 requests per minute on the free plan">
316
+ <button
317
+ type="button"
318
+ className="inline-flex h-5 w-5 items-center justify-center rounded-full text-zinc-400 transition-colors hover:bg-zinc-100 hover:text-zinc-600 dark:hover:bg-zinc-800 dark:hover:text-zinc-300"
319
+ aria-label="Rate limit info"
320
+ >
321
+ <HelpCircle className="h-3.5 w-3.5" />
322
+ </button>
323
+ </Tooltip>
324
+ <Badge variant="info">Pro</Badge>
325
+ </div>
326
+ <Tooltip content="⌘K to search · ⌘/ for commands">
327
+ <Button variant="ghost" size="sm" className="w-full justify-start">
328
+ <Info className="h-4 w-4" />
329
+ Keyboard shortcuts
330
+ </Button>
331
+ </Tooltip>
332
+ </CardContent>
333
+ </Card>
334
+ </DocsSectionGrid>
335
+ </DocsSection>
336
+
337
+ <DocsSection title="Disabled">
338
+ <Card className="max-w-md">
339
+ <CardContent className="flex items-center justify-between gap-4 pt-6">
340
+ <div>
341
+ <p className="text-sm font-medium text-zinc-800 dark:text-zinc-100">Export report</p>
342
+ <p className="mt-0.5 text-xs text-zinc-500 dark:text-zinc-400">Tooltip won&apos;t show when disabled</p>
343
+ </div>
344
+ <Tooltip content="Not available on your plan" disabled>
345
+ <Button size="sm" disabled>Export</Button>
346
+ </Tooltip>
347
+ </CardContent>
348
+ </Card>
349
+ </DocsSection>
350
+
351
+ <DocsApiSection>
352
+ <DocsApiTable title="API Reference" rows={API_ROWS} />
353
+ <DocsAccessibility items={ACCESSIBILITY_ITEMS} />
354
+ </DocsApiSection>
355
+ </DocsPage>
356
+ );
357
+ }
@@ -0,0 +1,21 @@
1
+ import { CodeBlock } from "@/components/ui/code-block";
2
+ import { DocsSection } from "@/components/docs/docs-page";
3
+
4
+ const USAGE_CODE = `import { Button } from 'ruler.ui'
5
+
6
+ export function App() {
7
+ return <Button variant="primary">Get Started</Button>
8
+ }`;
9
+
10
+ export function UsageSection() {
11
+ return (
12
+ <DocsSection
13
+ title="Usage"
14
+ description="Import components from ruler.ui and start building."
15
+ >
16
+ <div className="overflow-hidden rounded-2xl border border-zinc-200/50 bg-zinc-950 shadow-2xl dark:border-zinc-800/60">
17
+ <CodeBlock code={USAGE_CODE} filename="App.tsx" />
18
+ </div>
19
+ </DocsSection>
20
+ );
21
+ }
@@ -0,0 +1,9 @@
1
+ import type { SVGProps } from "react";
2
+
3
+ export function DiscordIcon(props: SVGProps<SVGSVGElement>) {
4
+ return (
5
+ <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" {...props}>
6
+ <path d="M20.32 4.37a19.79 19.79 0 0 0-4.89-1.52.07.07 0 0 0-.08.04c-.21.37-.44.86-.61 1.25a18.27 18.27 0 0 0-5.49 0 12.6 12.6 0 0 0-.62-1.25.08.08 0 0 0-.08-.04 19.74 19.74 0 0 0-4.88 1.51.07.07 0 0 0-.04.03C.53 9.05-.32 13.58.1 18.06a.08.08 0 0 0 .03.06 19.9 19.9 0 0 0 5.99 3.03.08.08 0 0 0 .08-.03c.46-.63.87-1.3 1.23-1.99a.08.08 0 0 0-.04-.11c-.65-.25-1.27-.55-1.87-.89a.08.08 0 0 1-.01-.13c.13-.09.25-.19.37-.29a.07.07 0 0 1 .08-.01c3.93 1.79 8.18 1.79 12.06 0a.07.07 0 0 1 .08.01c.12.1.24.2.37.29a.08.08 0 0 1-.01.13c-.6.34-1.22.64-1.87.89a.08.08 0 0 0-.04.11c.36.7.78 1.36 1.23 1.99a.08.08 0 0 0 .08.03 19.84 19.84 0 0 0 6-3.03.08.08 0 0 0 .03-.06c.5-5.18-.84-9.67-3.55-13.66a.06.06 0 0 0-.03-.03ZM8.02 15.33c-1.18 0-2.16-1.09-2.16-2.42 0-1.33.96-2.42 2.16-2.42 1.21 0 2.18 1.1 2.16 2.42 0 1.33-.95 2.42-2.16 2.42Zm7.97 0c-1.18 0-2.16-1.09-2.16-2.42 0-1.33.96-2.42 2.16-2.42 1.21 0 2.18 1.1 2.16 2.42 0 1.33-.95 2.42-2.16 2.42Z" />
7
+ </svg>
8
+ );
9
+ }
@@ -0,0 +1,9 @@
1
+ import type { SVGProps } from "react";
2
+
3
+ export function GithubIcon(props: SVGProps<SVGSVGElement>) {
4
+ return (
5
+ <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" {...props}>
6
+ <path d="M12 .5C5.65.5.5 5.65.5 12c0 5.09 3.29 9.4 7.86 10.93.57.1.79-.25.79-.55 0-.27-.01-1.17-.02-2.12-3.2.7-3.88-1.35-3.88-1.35-.52-1.33-1.28-1.68-1.28-1.68-1.04-.72.08-.7.08-.7 1.15.08 1.76 1.19 1.76 1.19 1.03 1.75 2.7 1.25 3.36.95.1-.75.4-1.25.72-1.53-2.55-.29-5.23-1.28-5.23-5.68 0-1.25.44-2.28 1.18-3.08-.12-.29-.51-1.46.11-3.05 0 0 .97-.31 3.18 1.18a11 11 0 0 1 5.78 0c2.2-1.49 3.17-1.18 3.17-1.18.63 1.59.24 2.76.12 3.05.74.8 1.18 1.83 1.18 3.08 0 4.41-2.69 5.38-5.25 5.67.41.36.78 1.06.78 2.14 0 1.55-.01 2.79-.01 3.17 0 .3.21.66.79.55A11.5 11.5 0 0 0 23.5 12c0-6.35-5.15-11.5-11.5-11.5Z" />
7
+ </svg>
8
+ );
9
+ }
@@ -0,0 +1,9 @@
1
+ import type { SVGProps } from "react";
2
+
3
+ export function XIcon(props: SVGProps<SVGSVGElement>) {
4
+ return (
5
+ <svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" {...props}>
6
+ <path d="M18.24 2.25h3.31l-7.23 8.26 8.5 11.24h-6.66l-5.21-6.82-5.96 6.82H1.68l7.73-8.84L1.25 2.25h6.83l4.71 6.23zm-1.16 17.52h1.83L7.08 4.13H5.12z" />
7
+ </svg>
8
+ );
9
+ }
@@ -0,0 +1,34 @@
1
+ import { Download } from "lucide-react";
2
+ import { PackageManagerTabs } from "@/components/ui/package-manager-tabs";
3
+
4
+ export function InstallationSection({
5
+ as: Heading = "h2",
6
+ caption,
7
+ }: {
8
+ as?: "h1" | "h2";
9
+ caption?: string;
10
+ }) {
11
+ return (
12
+ <div className="grid gap-8 lg:grid-cols-[minmax(0,300px)_1fr] lg:items-start">
13
+ <div>
14
+ <div className="flex items-center gap-2.5">
15
+ <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-zinc-900 text-white dark:bg-white dark:text-zinc-900">
16
+ <Download className="h-4 w-4" />
17
+ </span>
18
+ <Heading className="text-2xl font-bold tracking-tight text-zinc-900 dark:text-zinc-50">
19
+ Installation
20
+ </Heading>
21
+ </div>
22
+ <p className="mt-2 text-sm text-zinc-500 dark:text-zinc-400">
23
+ Install ruler.ui using your preferred package manager.
24
+ </p>
25
+ {caption ? (
26
+ <p className="mt-4 text-sm text-zinc-500 dark:text-zinc-400">{caption}</p>
27
+ ) : null}
28
+ </div>
29
+ <div>
30
+ <PackageManagerTabs />
31
+ </div>
32
+ </div>
33
+ );
34
+ }
@@ -0,0 +1,105 @@
1
+ import { Accessibility, Code2, SlidersHorizontal, ArrowRight } from "lucide-react";
2
+ import { GithubIcon } from "@/components/icons/github";
3
+
4
+ const FEATURES = [
5
+ {
6
+ index: "01",
7
+ icon: Accessibility,
8
+ title: "Accessible",
9
+ description: "Built with accessibility in mind from the ground up.",
10
+ tags: ["WCAG 2.2", "WAI-ARIA"],
11
+ },
12
+ {
13
+ index: "02",
14
+ icon: SlidersHorizontal,
15
+ title: "Customizable",
16
+ description: "Easily customize components to match your brand.",
17
+ tags: ["THEMING", "VARIANTS"],
18
+ },
19
+ {
20
+ index: "03",
21
+ icon: Code2,
22
+ title: "Developer First",
23
+ description: "TypeScript-first with excellent developer experience.",
24
+ tags: ["TYPESCRIPT", "DX"],
25
+ },
26
+ {
27
+ index: "04",
28
+ icon: GithubIcon,
29
+ title: "Open Source",
30
+ description: "MIT licensed and open for everyone.",
31
+ tags: ["GITHUB"],
32
+ },
33
+ ];
34
+
35
+ export function FeatureHighlights() {
36
+ return (
37
+ <section className="w-full min-w-0 bg-white px-4 py-16 font-sans dark:bg-zinc-950 sm:px-6 sm:py-20">
38
+ <div className="mx-auto max-w-7xl">
39
+ <div className="mb-16 text-center">
40
+ <div className="mb-4 flex items-center justify-center gap-4">
41
+ <div className="h-px w-8 bg-zinc-300 dark:bg-zinc-700" />
42
+ <span className="text-[10px] font-bold uppercase tracking-[0.2em] text-zinc-500 dark:text-zinc-400">
43
+ Built for modern teams
44
+ </span>
45
+ <div className="h-px w-8 bg-zinc-300 dark:bg-zinc-700" />
46
+ </div>
47
+ <h2 className="mb-4 text-3xl font-bold text-zinc-900 dark:text-white md:text-4xl">
48
+ Everything you need to build better interfaces
49
+ </h2>
50
+ <p className="mx-auto max-w-2xl text-sm text-zinc-500 dark:text-zinc-400">
51
+ Thoughtful primitives designed for accessibility, flexibility, and a
52
+ great developer experience.
53
+ </p>
54
+ </div>
55
+
56
+ <div className="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-4">
57
+ {FEATURES.map((feature) => (
58
+ <div
59
+ key={feature.title}
60
+ className="group relative flex flex-col rounded-2xl border border-zinc-200/80 p-7 shadow-[0_2px_8px_rgba(0,0,0,0.04)] transition-all duration-200 hover:-translate-y-0.5 hover:border-zinc-300 hover:shadow-[0_8px_24px_rgba(0,0,0,0.08)] dark:border-zinc-800/80 dark:shadow-[0_2px_8px_rgba(0,0,0,0.25)] dark:hover:border-zinc-700 dark:hover:shadow-[0_8px_24px_rgba(0,0,0,0.4)]"
61
+ >
62
+ <div className="flex items-center justify-between">
63
+ <span className="flex h-11 w-11 items-center justify-center rounded-xl border border-zinc-200 bg-zinc-50 text-zinc-900 dark:border-white/10 dark:bg-white/[0.06] dark:text-zinc-100">
64
+ <feature.icon className="h-5 w-5" />
65
+ </span>
66
+ <span className="text-sm font-medium text-zinc-400 dark:text-zinc-500">
67
+ {feature.index}
68
+ </span>
69
+ </div>
70
+
71
+ <h3 className="mt-5 text-lg font-bold text-zinc-900 dark:text-white">
72
+ {feature.title}
73
+ </h3>
74
+ <p className="mt-2 text-sm leading-relaxed text-zinc-500 dark:text-zinc-400">
75
+ {feature.description}
76
+ </p>
77
+
78
+ <div className="mt-auto flex items-center justify-between border-t border-zinc-100 pt-4 dark:border-zinc-800/80">
79
+ <div className="flex items-center gap-2">
80
+ {feature.tags.map((tag, tagIdx) => (
81
+ <span
82
+ key={tag}
83
+ className="flex items-center text-[10px] font-bold text-zinc-500 dark:text-zinc-400"
84
+ >
85
+ {tag}
86
+ {tagIdx < feature.tags.length - 1 && (
87
+ <span className="mx-2 text-zinc-300 dark:text-zinc-700">
88
+
89
+ </span>
90
+ )}
91
+ </span>
92
+ ))}
93
+ </div>
94
+ <ArrowRight
95
+ size={16}
96
+ className="text-zinc-400 transition-transform group-hover:translate-x-0.5 group-hover:text-zinc-900 dark:text-zinc-500 dark:group-hover:text-white"
97
+ />
98
+ </div>
99
+ </div>
100
+ ))}
101
+ </div>
102
+ </div>
103
+ </section>
104
+ );
105
+ }
@@ -0,0 +1,45 @@
1
+ import { CheckCircle2, FileCode2, Paintbrush } from "lucide-react";
2
+ import { GithubIcon } from "@/components/icons/github";
3
+
4
+ const ITEMS = [
5
+ {
6
+ icon: FileCode2,
7
+ title: "TypeScript First",
8
+ description: "Built with TypeScript",
9
+ },
10
+ {
11
+ icon: CheckCircle2,
12
+ title: "Accessible",
13
+ description: "WAI-ARIA compliant",
14
+ },
15
+ {
16
+ icon: Paintbrush,
17
+ title: "Customizable",
18
+ description: "Theming and variants",
19
+ },
20
+ {
21
+ icon: GithubIcon,
22
+ title: "Open Source",
23
+ description: "MIT Licensed",
24
+ },
25
+ ];
26
+
27
+ export function FooterStrip() {
28
+ return (
29
+ <div className="mb-16 rounded-2xl bg-zinc-950 p-5 sm:p-10">
30
+ <div className="grid grid-cols-2 gap-6 sm:grid-cols-4">
31
+ {ITEMS.map((item) => (
32
+ <div key={item.title} className="flex min-w-0 items-center gap-3">
33
+ <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border border-white/10 bg-white/[0.06] text-zinc-100">
34
+ <item.icon className="h-4 w-4" />
35
+ </span>
36
+ <div className="min-w-0">
37
+ <p className="text-sm font-semibold text-zinc-50">{item.title}</p>
38
+ <p className="text-xs text-zinc-400">{item.description}</p>
39
+ </div>
40
+ </div>
41
+ ))}
42
+ </div>
43
+ </div>
44
+ );
45
+ }
@@ -0,0 +1,14 @@
1
+ import { Navbar } from "@/components/landing/navbar";
2
+ import { Hero } from "@/components/landing/hero";
3
+ import { HeroSpotlight } from "@/components/landing/hero-spotlight";
4
+
5
+ export function HeroCard() {
6
+ return (
7
+ <HeroSpotlight>
8
+ <div className="relative px-4 pb-16 pt-5 sm:px-10 sm:pb-24 sm:pt-8">
9
+ <Navbar />
10
+ <Hero />
11
+ </div>
12
+ </HeroSpotlight>
13
+ );
14
+ }