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
package/app/page.tsx ADDED
@@ -0,0 +1,34 @@
1
+ import { HeroCard } from "@/components/landing/hero-card";
2
+ import { FeatureHighlights } from "@/components/landing/feature-highlights";
3
+ import { InstallationSection } from "@/components/installation-section";
4
+ import { QuickStartSection } from "@/components/landing/quick-start-section";
5
+ import { FooterStrip } from "@/components/landing/footer-strip";
6
+ import { SiteFooter } from "@/components/landing/site-footer";
7
+
8
+ export default function Home() {
9
+ return (
10
+ <main className="min-w-0 overflow-x-clip bg-white dark:bg-zinc-950">
11
+ <div className="px-3 pt-3 sm:px-6 sm:pt-4 lg:px-8 xl:px-12">
12
+ <HeroCard />
13
+ </div>
14
+
15
+ <FeatureHighlights />
16
+
17
+ <div className="mx-auto max-w-7xl px-6 sm:px-8 lg:px-10 xl:px-12">
18
+ <div className="py-10 sm:py-14">
19
+ <div className="rounded-3xl border border-zinc-200/70 bg-gradient-to-b from-zinc-50/80 to-white p-5 shadow-[0_2px_8px_rgba(0,0,0,0.04)] dark:border-zinc-800/70 dark:from-zinc-900/30 dark:to-zinc-950 dark:shadow-[0_2px_8px_rgba(0,0,0,0.2)] sm:p-12">
20
+ <InstallationSection
21
+ caption="ruler.ui is built with TypeScript and works with React 18+."
22
+ />
23
+ </div>
24
+ </div>
25
+ <QuickStartSection />
26
+ <FooterStrip />
27
+ </div>
28
+
29
+ <div className="mx-auto max-w-7xl px-6 sm:px-8 lg:px-10 xl:px-12">
30
+ <SiteFooter />
31
+ </div>
32
+ </main>
33
+ );
34
+ }
@@ -0,0 +1,10 @@
1
+ import { createOgImage, ogAlt, ogSize } from "@/lib/og-image";
2
+
3
+ export const runtime = "nodejs";
4
+ export const alt = ogAlt;
5
+ export const size = ogSize;
6
+ export const contentType = "image/png";
7
+
8
+ export default async function Image() {
9
+ return createOgImage();
10
+ }
@@ -0,0 +1,352 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { Check, ChevronsUpDown, Copy, HelpCircle, PanelLeft, Truck } from "lucide-react";
5
+ import { Accordion, AccordionItem, AccordionNested, AccordionLink } from "@/components/ui/accordion";
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
+ } from "@/components/docs/docs-page";
17
+ import { cn } from "@/lib/cn";
18
+
19
+ const ACCORDION_API_ROWS = [
20
+ { prop: "children", type: "ReactNode", default: "—", description: "One or more AccordionItem elements" },
21
+ { prop: "defaultValue", type: "string | null", default: "null", description: "Value of the item expanded on mount" },
22
+ { prop: "className", type: "string", default: "—", description: "Additional CSS classes for the wrapper" },
23
+ ];
24
+
25
+ const ITEM_API_ROWS = [
26
+ { prop: "value", type: "string", default: "—", description: "Unique identifier used to track open state" },
27
+ { prop: "title", type: "string", default: "—", description: "Text shown in the trigger row" },
28
+ { prop: "children", type: "ReactNode", default: "—", description: "Panel content revealed when open" },
29
+ { prop: "variant", type: '"plus" | "chevron"', default: '"plus"', description: "Icon and trigger style" },
30
+ ];
31
+
32
+ const NESTED_API_ROWS = [
33
+ { prop: "title", type: "string", default: "—", description: "Label for the branch node" },
34
+ { prop: "children", type: "ReactNode", default: "—", description: "Nested AccordionNested / AccordionLink items" },
35
+ { prop: "level", type: "number", default: "0", description: "Indentation depth — increment for each nested level" },
36
+ ];
37
+
38
+ const LINK_API_ROWS = [
39
+ { prop: "title", type: "string", default: "—", description: "Label for the leaf navigation item" },
40
+ { prop: "level", type: "number", default: "0", description: "Indentation depth — match the parent's level + 1" },
41
+ ];
42
+
43
+ const ACCESSIBILITY_ITEMS = [
44
+ "Every trigger renders as a native <button>, so it's reachable and activatable via keyboard by default.",
45
+ "aria-expanded reflects open state and aria-controls links each trigger to its panel for screen readers.",
46
+ "Panels animate by measuring scrollHeight rather than toggling display, so content never clips mid-transition.",
47
+ "Only one item stays open at a time within an Accordion — keeps focus narrow for FAQ-style content.",
48
+ "Nested menus support arbitrary depth via the level prop and indent children automatically.",
49
+ ];
50
+
51
+ const FAQ = [
52
+ {
53
+ value: "1",
54
+ title: "Can I change my plan later?",
55
+ answer: "Yes — upgrade or downgrade at any time from your billing settings. Changes are prorated automatically.",
56
+ },
57
+ {
58
+ value: "2",
59
+ title: "Do you offer a free trial?",
60
+ answer: "Every plan includes a 14-day trial with full feature access, no credit card required.",
61
+ },
62
+ {
63
+ value: "3",
64
+ title: "Is my data encrypted?",
65
+ answer: "All data is encrypted in transit with TLS 1.3 and at rest with AES-256, on every plan.",
66
+ },
67
+ {
68
+ value: "4",
69
+ title: "What happens if I cancel?",
70
+ answer: "Your workspace stays active until the end of the current billing period, then moves to the free tier automatically.",
71
+ },
72
+ ];
73
+
74
+ type PlaygroundVariant = "plus" | "chevron";
75
+
76
+ const PLAYGROUND_VARIANTS: { value: PlaygroundVariant; label: string }[] = [
77
+ { value: "plus", label: "Plus" },
78
+ { value: "chevron", label: "Chevron" },
79
+ ];
80
+
81
+ const ITEM_COUNTS = [2, 3] as const;
82
+
83
+ function codeFor(variant: PlaygroundVariant, itemCount: (typeof ITEM_COUNTS)[number], defaultOpen: boolean) {
84
+ const items = FAQ.slice(0, itemCount);
85
+ const props = [variant !== "plus" && `variant="${variant}"`].filter(Boolean).join(" ");
86
+ const openProp = defaultOpen ? ` defaultValue="${items[0].value}"` : "";
87
+ const children = items
88
+ .map((item) => {
89
+ const itemProps = [`value="${item.value}"`, `title="${item.title}"`, props].filter(Boolean).join(" ");
90
+ return ` <AccordionItem ${itemProps}>\n ${item.answer}\n </AccordionItem>`;
91
+ })
92
+ .join("\n");
93
+ return `<Accordion${openProp}>\n${children}\n</Accordion>`;
94
+ }
95
+
96
+ export function AccordionDocPage() {
97
+ const [variant, setVariant] = useState<"plus" | "chevron">("plus");
98
+ const [playgroundVariant, setPlaygroundVariant] = useState<PlaygroundVariant>("plus");
99
+ const [itemCount, setItemCount] = useState<(typeof ITEM_COUNTS)[number]>(3);
100
+ const [defaultOpen, setDefaultOpen] = useState(true);
101
+ const [copied, setCopied] = useState(false);
102
+
103
+ const snippet = codeFor(playgroundVariant, itemCount, defaultOpen);
104
+ const playgroundItems = FAQ.slice(0, itemCount);
105
+
106
+ function copySnippet() {
107
+ navigator.clipboard.writeText(snippet).catch(() => {});
108
+ setCopied(true);
109
+ setTimeout(() => setCopied(false), 1200);
110
+ }
111
+
112
+ return (
113
+ <DocsPage>
114
+ <DocsPageHeader
115
+ icon={ChevronsUpDown}
116
+ title="Accordion"
117
+ description="A vertically stacked list of items that can be expanded or collapsed to reveal more content."
118
+ />
119
+
120
+ <DocsSection
121
+ first
122
+ title="Playground"
123
+ description="Pick a trigger style and item count, then copy the exact markup — live."
124
+ >
125
+ <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)]">
126
+ <div className="relative flex min-h-[11rem] items-center justify-center overflow-hidden bg-zinc-50 p-6 dark:bg-zinc-900/40">
127
+ <div
128
+ aria-hidden
129
+ className="pointer-events-none absolute inset-0 opacity-[0.4] dark:opacity-[0.15]"
130
+ style={{
131
+ backgroundImage:
132
+ "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)",
133
+ backgroundSize: "24px 24px",
134
+ }}
135
+ />
136
+ <div
137
+ key={`${playgroundVariant}-${itemCount}-${defaultOpen}`}
138
+ className="relative w-full max-w-md animate-[pagination-pop_0.2s_ease-out] rounded-xl border border-zinc-200 bg-white px-5 dark:border-zinc-800 dark:bg-zinc-950"
139
+ >
140
+ <Accordion defaultValue={defaultOpen ? playgroundItems[0].value : null}>
141
+ {playgroundItems.map((item) => (
142
+ <AccordionItem key={item.value} value={item.value} title={item.title} variant={playgroundVariant}>
143
+ {item.answer}
144
+ </AccordionItem>
145
+ ))}
146
+ </Accordion>
147
+ </div>
148
+ </div>
149
+
150
+ <div className="flex flex-col gap-5 border-t border-zinc-200 p-5 dark:border-zinc-800">
151
+ <div className="flex flex-wrap items-start justify-between gap-6">
152
+ <div>
153
+ <p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
154
+ Variant
155
+ </p>
156
+ <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">
157
+ {PLAYGROUND_VARIANTS.map((v) => (
158
+ <button
159
+ key={v.value}
160
+ type="button"
161
+ onClick={() => setPlaygroundVariant(v.value)}
162
+ className={cn(
163
+ "rounded-md px-3 py-1.5 text-xs font-medium transition-colors",
164
+ playgroundVariant === v.value
165
+ ? "bg-white text-zinc-900 shadow-sm dark:bg-zinc-800 dark:text-white"
166
+ : "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200"
167
+ )}
168
+ >
169
+ {v.label}
170
+ </button>
171
+ ))}
172
+ </div>
173
+ </div>
174
+
175
+ <div>
176
+ <p className="mb-2 text-xs font-medium uppercase tracking-wide text-zinc-400 dark:text-zinc-500">
177
+ Items
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
+ {ITEM_COUNTS.map((count) => (
181
+ <button
182
+ key={count}
183
+ type="button"
184
+ onClick={() => setItemCount(count)}
185
+ className={cn(
186
+ "rounded-md px-3 py-1.5 text-xs font-medium transition-colors",
187
+ itemCount === count
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
+ {count}
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
+ Default open
201
+ </p>
202
+ <button
203
+ type="button"
204
+ onClick={() => setDefaultOpen((v) => !v)}
205
+ className={cn(
206
+ "flex h-[2.125rem] items-center gap-2 rounded-lg border px-3 text-xs font-medium transition-colors",
207
+ defaultOpen
208
+ ? "border-zinc-900 bg-zinc-900 text-white dark:border-white dark:bg-white dark:text-zinc-900"
209
+ : "border-zinc-200 text-zinc-500 hover:border-zinc-300 dark:border-zinc-700 dark:text-zinc-400"
210
+ )}
211
+ >
212
+ {defaultOpen ? "On" : "Off"}
213
+ </button>
214
+ </div>
215
+ </div>
216
+
217
+ <div className="flex items-center justify-between gap-3 rounded-xl bg-zinc-900 px-4 py-3 dark:bg-black">
218
+ <pre className="overflow-x-auto font-mono text-[13px] leading-relaxed text-zinc-100">
219
+ <code>{snippet}</code>
220
+ </pre>
221
+ <Tooltip content={copied ? "Copied!" : "Copy code"}>
222
+ <button
223
+ type="button"
224
+ onClick={copySnippet}
225
+ 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"
226
+ >
227
+ {copied ? <Check className="h-4 w-4 text-emerald-400" /> : <Copy className="h-4 w-4" />}
228
+ </button>
229
+ </Tooltip>
230
+ </div>
231
+ </div>
232
+ </div>
233
+ </DocsSection>
234
+
235
+ <DocsSection
236
+ title="Try it"
237
+ description="One trigger style, swapped live — pick whichever matches your layout's density."
238
+ >
239
+ <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)]">
240
+ <div className="flex flex-wrap items-center justify-between gap-4 border-b border-zinc-200 bg-zinc-50/70 px-5 py-4 dark:border-zinc-800 dark:bg-zinc-900/40">
241
+ <div className="flex items-center gap-3">
242
+ <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-lg border border-zinc-200 bg-white text-zinc-500 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-400">
243
+ <HelpCircle className="h-4 w-4" />
244
+ </span>
245
+ <div>
246
+ <p className="text-sm font-semibold text-zinc-900 dark:text-white">Frequently asked questions</p>
247
+ <p className="text-xs text-zinc-500 dark:text-zinc-400">Billing and account basics.</p>
248
+ </div>
249
+ </div>
250
+
251
+ <div className="flex flex-wrap items-center gap-1 rounded-lg border border-zinc-200 bg-white p-1 dark:border-zinc-700 dark:bg-zinc-950">
252
+ {(["plus", "chevron"] as const).map((v) => (
253
+ <button
254
+ key={v}
255
+ type="button"
256
+ onClick={() => setVariant(v)}
257
+ className={cn(
258
+ "rounded-md px-3 py-1.5 text-xs font-medium capitalize transition-colors",
259
+ variant === v
260
+ ? "bg-zinc-900 text-white dark:bg-white dark:text-zinc-900"
261
+ : "text-zinc-500 hover:text-zinc-800 dark:text-zinc-400 dark:hover:text-zinc-200"
262
+ )}
263
+ >
264
+ {v}
265
+ </button>
266
+ ))}
267
+ </div>
268
+ </div>
269
+
270
+ <div className="bg-white px-5 dark:bg-zinc-950">
271
+ <Accordion defaultValue="1">
272
+ {FAQ.map((item) => (
273
+ <AccordionItem key={item.value} value={item.value} title={item.title} variant={variant}>
274
+ {item.answer}
275
+ </AccordionItem>
276
+ ))}
277
+ </Accordion>
278
+ </div>
279
+ </div>
280
+ </DocsSection>
281
+
282
+ <DocsSection
283
+ title="Hierarchical / Nested"
284
+ description="A menu-like tree with unlimited depth — ideal for sidebar and docs navigation."
285
+ >
286
+ <Card className="max-w-md overflow-hidden p-0">
287
+ <div className="flex items-center gap-2 border-b border-zinc-100 bg-zinc-50/80 px-4 py-3 dark:border-zinc-800 dark:bg-zinc-900/40">
288
+ <PanelLeft className="h-3.5 w-3.5 text-zinc-400 dark:text-zinc-500" />
289
+ <span className="text-xs font-medium text-zinc-500 dark:text-zinc-400">Documentation</span>
290
+ </div>
291
+ <div className="p-3">
292
+ <AccordionNested title="Getting Started">
293
+ <AccordionLink title="Introduction" level={1} />
294
+ <AccordionLink title="Installation" level={1} />
295
+ </AccordionNested>
296
+ <AccordionNested title="Components">
297
+ <AccordionLink title="Button" level={1} />
298
+ <AccordionNested title="Overlays" level={1}>
299
+ <AccordionLink title="Dialog" level={2} />
300
+ <AccordionLink title="Modal" level={2} />
301
+ </AccordionNested>
302
+ </AccordionNested>
303
+ <AccordionLink title="Theming" />
304
+ </div>
305
+ </Card>
306
+ </DocsSection>
307
+
308
+ <DocsSection
309
+ title="In context"
310
+ description="A shipping & returns block, the way it'd actually ship on a product page."
311
+ >
312
+ <Card className="max-w-2xl overflow-hidden p-0">
313
+ <CardHeader className="flex-row items-center gap-3 border-b border-zinc-100 dark:border-zinc-800">
314
+ <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">
315
+ <Truck className="h-4 w-4" />
316
+ </span>
317
+ <div>
318
+ <CardTitle className="text-base">Shipping &amp; Returns</CardTitle>
319
+ <CardDescription>Everything covered under our standard policy.</CardDescription>
320
+ </div>
321
+ <Badge variant="outline" className="ml-auto">
322
+ Policy v2
323
+ </Badge>
324
+ </CardHeader>
325
+ <CardContent className="pt-2">
326
+ <Accordion defaultValue="ship">
327
+ <AccordionItem value="ship" title="How long does shipping take?">
328
+ Orders ship within 1 business day and arrive in 3–5 business days domestically, or 7–14 days
329
+ internationally.
330
+ </AccordionItem>
331
+ <AccordionItem value="return" title="What's your return policy?">
332
+ Unused items in original packaging can be returned within 30 days for a full refund — no restocking
333
+ fee.
334
+ </AccordionItem>
335
+ <AccordionItem value="warranty" title="Is there a warranty?">
336
+ Every order includes a 1-year limited warranty covering manufacturing defects.
337
+ </AccordionItem>
338
+ </Accordion>
339
+ </CardContent>
340
+ </Card>
341
+ </DocsSection>
342
+
343
+ <DocsApiSection>
344
+ <DocsApiTable title="API Reference — Accordion" rows={ACCORDION_API_ROWS} />
345
+ <DocsApiTable title="API Reference — AccordionItem" rows={ITEM_API_ROWS} />
346
+ <DocsApiTable title="API Reference — AccordionNested" rows={NESTED_API_ROWS} />
347
+ <DocsApiTable title="API Reference — AccordionLink" rows={LINK_API_ROWS} />
348
+ <DocsAccessibility items={ACCESSIBILITY_ITEMS} />
349
+ </DocsApiSection>
350
+ </DocsPage>
351
+ );
352
+ }