@stampui/blocks 1.0.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 (107) hide show
  1. package/dist/components/ai-chat-shell.d.ts +1 -0
  2. package/dist/components/ai-chat-shell.js +23 -0
  3. package/dist/components/prompt-input.d.ts +5 -0
  4. package/dist/components/prompt-input.js +47 -0
  5. package/dist/components/registry-card.d.ts +6 -0
  6. package/dist/components/registry-card.js +15 -0
  7. package/dist/components/registry-explorer.d.ts +8 -0
  8. package/dist/components/registry-explorer.js +38 -0
  9. package/dist/components/token-stream.d.ts +7 -0
  10. package/dist/components/token-stream.js +21 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.js +23 -0
  13. package/dist/manifests.d.ts +3 -0
  14. package/dist/manifests.js +1666 -0
  15. package/dist/types.d.ts +44 -0
  16. package/dist/types.js +2 -0
  17. package/package.json +28 -0
  18. package/src/components/blocks/ai-chat-shell.tsx +97 -0
  19. package/src/components/blocks/auth-panel.tsx +203 -0
  20. package/src/components/blocks/feature-grid.tsx +122 -0
  21. package/src/components/blocks/hero-section.tsx +73 -0
  22. package/src/components/blocks/notification-center.tsx +185 -0
  23. package/src/components/blocks/onboarding-flow.tsx +230 -0
  24. package/src/components/blocks/pricing-section.tsx +135 -0
  25. package/src/components/blocks/project-command-center.tsx +188 -0
  26. package/src/components/blocks/prompt-input.tsx +81 -0
  27. package/src/components/blocks/registry-card.tsx +104 -0
  28. package/src/components/blocks/registry-explorer.tsx +78 -0
  29. package/src/components/blocks/settings-layout.tsx +178 -0
  30. package/src/components/blocks/stats-strip.tsx +100 -0
  31. package/src/components/blocks/token-stream.tsx +42 -0
  32. package/src/components/blocks/usage-card.tsx +116 -0
  33. package/src/components/core/accordion.tsx +58 -0
  34. package/src/components/core/alert-dialog.tsx +113 -0
  35. package/src/components/core/alert.tsx +48 -0
  36. package/src/components/core/animated-number.tsx +77 -0
  37. package/src/components/core/aspect-ratio.tsx +20 -0
  38. package/src/components/core/avatar-stack.tsx +61 -0
  39. package/src/components/core/avatar.tsx +90 -0
  40. package/src/components/core/badge.tsx +39 -0
  41. package/src/components/core/breadcrumb.tsx +63 -0
  42. package/src/components/core/button-group.tsx +37 -0
  43. package/src/components/core/button.tsx +110 -0
  44. package/src/components/core/calendar.tsx +143 -0
  45. package/src/components/core/card.tsx +60 -0
  46. package/src/components/core/carousel.tsx +170 -0
  47. package/src/components/core/chart.tsx +377 -0
  48. package/src/components/core/checkbox.tsx +64 -0
  49. package/src/components/core/collapsible.tsx +30 -0
  50. package/src/components/core/combobox.tsx +114 -0
  51. package/src/components/core/command-box.tsx +22 -0
  52. package/src/components/core/command.tsx +165 -0
  53. package/src/components/core/confirm-action.tsx +94 -0
  54. package/src/components/core/context-menu.tsx +139 -0
  55. package/src/components/core/copy-button.tsx +41 -0
  56. package/src/components/core/data-table.tsx +173 -0
  57. package/src/components/core/date-picker.tsx +73 -0
  58. package/src/components/core/dialog.tsx +83 -0
  59. package/src/components/core/drawer.tsx +87 -0
  60. package/src/components/core/dropdown-menu.tsx +147 -0
  61. package/src/components/core/empty.tsx +34 -0
  62. package/src/components/core/field.tsx +39 -0
  63. package/src/components/core/file-upload.tsx +143 -0
  64. package/src/components/core/hover-card.tsx +31 -0
  65. package/src/components/core/inline-edit.tsx +104 -0
  66. package/src/components/core/input-group.tsx +47 -0
  67. package/src/components/core/input-otp.tsx +108 -0
  68. package/src/components/core/input.tsx +37 -0
  69. package/src/components/core/kbd.tsx +47 -0
  70. package/src/components/core/label.tsx +28 -0
  71. package/src/components/core/marquee.tsx +61 -0
  72. package/src/components/core/menubar.tsx +120 -0
  73. package/src/components/core/multi-select.tsx +145 -0
  74. package/src/components/core/native-select.tsx +27 -0
  75. package/src/components/core/navigation-menu.tsx +130 -0
  76. package/src/components/core/number-stepper.tsx +80 -0
  77. package/src/components/core/pagination.tsx +80 -0
  78. package/src/components/core/password-input.tsx +90 -0
  79. package/src/components/core/popover.tsx +34 -0
  80. package/src/components/core/progress.tsx +63 -0
  81. package/src/components/core/radio-group.tsx +77 -0
  82. package/src/components/core/resizable.tsx +250 -0
  83. package/src/components/core/scroll-area.tsx +38 -0
  84. package/src/components/core/select.tsx +128 -0
  85. package/src/components/core/separator.tsx +47 -0
  86. package/src/components/core/sheet.tsx +118 -0
  87. package/src/components/core/sidebar.tsx +129 -0
  88. package/src/components/core/skeleton.tsx +32 -0
  89. package/src/components/core/slider.tsx +97 -0
  90. package/src/components/core/sonner.tsx +29 -0
  91. package/src/components/core/spinner.tsx +60 -0
  92. package/src/components/core/status-pulse.tsx +67 -0
  93. package/src/components/core/stepper.tsx +111 -0
  94. package/src/components/core/switch.tsx +72 -0
  95. package/src/components/core/table.tsx +104 -0
  96. package/src/components/core/tabs.tsx +55 -0
  97. package/src/components/core/tag-input.tsx +93 -0
  98. package/src/components/core/textarea.tsx +44 -0
  99. package/src/components/core/timeline.tsx +81 -0
  100. package/src/components/core/toggle-group.tsx +56 -0
  101. package/src/components/core/toggle.tsx +66 -0
  102. package/src/components/core/tooltip.tsx +31 -0
  103. package/src/components/core/typing-indicator.tsx +51 -0
  104. package/src/index.ts +8 -0
  105. package/src/manifests.ts +1682 -0
  106. package/src/types.ts +58 -0
  107. package/src/ui.ts +13 -0
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The tier of a block. Determines CLI install behavior and registry visibility.
3
+ * - free: Publicly installable by anyone.
4
+ * - pro: Requires a valid StampUI license key.
5
+ * - new: Recently published free block.
6
+ * - locked: Explicitly gated (e.g. not yet released).
7
+ */
8
+ export type BlockStatus = "free" | "pro" | "new" | "locked";
9
+ /**
10
+ * A single file reference included in a block install.
11
+ */
12
+ export interface BlockFile {
13
+ path: string;
14
+ type: "block" | "util" | "hook" | "registry:ui";
15
+ }
16
+ /**
17
+ * The canonical manifest for a StampUI registry block.
18
+ * This is the single source of truth consumed by the CLI, web registry, and preview system.
19
+ */
20
+ export interface BlockManifest {
21
+ slug: string;
22
+ title: string;
23
+ description: string;
24
+ category: string;
25
+ tags: string[];
26
+ version: string;
27
+ updatedAt: string;
28
+ minCliVersion?: string;
29
+ changelog?: string[];
30
+ status: BlockStatus;
31
+ licenseRequired?: boolean;
32
+ difficulty: "beginner" | "intermediate" | "advanced";
33
+ frameworks: string[];
34
+ dependencies: string[];
35
+ files: BlockFile[];
36
+ tokens: string[];
37
+ supportsDarkMode: boolean;
38
+ supportsLightMode: boolean;
39
+ promptReady: boolean;
40
+ /** Relative path to a local preview asset, e.g. /previews/token-stream.svg */
41
+ previewPath?: string;
42
+ /** CLI and Web specific logic, populated at runtime if needed */
43
+ source?: string;
44
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@stampui/blocks",
3
+ "version": "1.0.0",
4
+ "description": "StampUI blocks, registry, and source files",
5
+ "files": ["dist", "src"],
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "dev": "tsc -w",
11
+ "prepublishOnly": "tsc"
12
+ },
13
+ "peerDependencies": {
14
+ "react": "^18.2.0 || ^19.0.0",
15
+ "react-dom": "^18.2.0 || ^19.0.0"
16
+ },
17
+ "dependencies": {
18
+ "lucide-react": "^0.475.0"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "^20.0.0",
22
+ "@types/react": "^19.2.14",
23
+ "typescript": "^5.0.0"
24
+ },
25
+ "publishConfig": {
26
+ "access": "public"
27
+ }
28
+ }
@@ -0,0 +1,97 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Menu, Plus, MessageSquare, Settings } from "lucide-react"
5
+ import { cx } from "@/lib/cx"
6
+ import { Button } from "@/components/core/button"
7
+ import { PromptInput } from "@/components/blocks/prompt-input"
8
+ import { TokenStream } from "@/components/blocks/token-stream"
9
+
10
+ export function AIChatShell() {
11
+ const [sidebarOpen, setSidebarOpen] = React.useState(true)
12
+ const [messages, setMessages] = React.useState([
13
+ { role: "assistant", content: "Hello! I am ready to help you build your next app. What would you like to create today?" }
14
+ ])
15
+
16
+ const handleSubmit = (val: string) => {
17
+ setMessages([...messages, { role: "user", content: val }])
18
+ setTimeout(() => {
19
+ setMessages(prev => [...prev, { role: "assistant", content: "I can certainly help with that! Let's break down the requirements..." }])
20
+ }, 600)
21
+ }
22
+
23
+ return (
24
+ <div className="flex h-[600px] w-full overflow-hidden rounded-xl border border-border bg-background shadow-xl">
25
+ {/* Sidebar */}
26
+ <div
27
+ className={cx(
28
+ "flex flex-col border-r border-border bg-muted/20 transition-all duration-300",
29
+ sidebarOpen ? "w-64" : "w-0 overflow-hidden border-r-0"
30
+ )}
31
+ >
32
+ <div className="flex h-14 items-center justify-between px-4 border-b border-border">
33
+ <span className="font-semibold text-sm">Chats</span>
34
+ <Button variant="ghost" size="icon" className="h-8 w-8">
35
+ <Plus className="h-4 w-4" />
36
+ </Button>
37
+ </div>
38
+ <div className="flex-1 overflow-y-auto p-2 space-y-1">
39
+ <Button variant="ghost" className="w-full justify-start font-normal text-sm text-muted-foreground hover:text-foreground">
40
+ <MessageSquare className="mr-2 h-4 w-4" /> E-commerce Data Model
41
+ </Button>
42
+ <Button variant="ghost" className="w-full justify-start font-normal text-sm text-muted-foreground hover:text-foreground">
43
+ <MessageSquare className="mr-2 h-4 w-4" /> Fix Next.js Hydration Error
44
+ </Button>
45
+ </div>
46
+ <div className="p-2 border-t border-border">
47
+ <Button variant="ghost" className="w-full justify-start text-sm">
48
+ <Settings className="mr-2 h-4 w-4" /> Settings
49
+ </Button>
50
+ </div>
51
+ </div>
52
+
53
+ {/* Main Chat Area */}
54
+ <div className="flex flex-1 flex-col bg-background">
55
+ <div className="flex h-14 items-center border-b border-border px-4">
56
+ <Button
57
+ variant="ghost"
58
+ size="icon"
59
+ className="h-8 w-8 mr-2 md:hidden"
60
+ onClick={() => setSidebarOpen(!sidebarOpen)}
61
+ >
62
+ <Menu className="h-4 w-4" />
63
+ </Button>
64
+ <span className="font-medium text-sm">New Chat</span>
65
+ </div>
66
+
67
+ <div className="flex-1 overflow-y-auto p-4 md:p-6 space-y-6">
68
+ {messages.map((m, i) => (
69
+ <div key={i} className={cx("flex w-full", m.role === "user" ? "justify-end" : "justify-start")}>
70
+ <div className={cx(
71
+ "max-w-[80%] rounded-2xl px-5 py-3 text-sm",
72
+ m.role === "user"
73
+ ? "bg-primary text-primary-foreground"
74
+ : "bg-muted/50 text-foreground border border-border/50"
75
+ )}>
76
+ {m.role === "assistant" && i === messages.length - 1 ? (
77
+ <TokenStream content={m.content} speed={20} />
78
+ ) : (
79
+ <span className="whitespace-pre-wrap">{m.content}</span>
80
+ )}
81
+ </div>
82
+ </div>
83
+ ))}
84
+ </div>
85
+
86
+ <div className="p-4 md:p-6 pt-0">
87
+ <div className="mx-auto max-w-3xl">
88
+ <PromptInput onSubmit={handleSubmit} />
89
+ <div className="mt-2 text-center text-xs text-muted-foreground">
90
+ AI can make mistakes. Verify important information.
91
+ </div>
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ )
97
+ }
@@ -0,0 +1,203 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Eye, EyeOff } from "lucide-react"
5
+ import { cx } from "@/lib/cx"
6
+
7
+ type AuthMode = "login" | "register"
8
+
9
+ interface AuthPanelProps {
10
+ defaultMode?: AuthMode
11
+ onLogin?: (email: string, password: string) => Promise<void> | void
12
+ onRegister?: (email: string, password: string, name: string) => Promise<void> | void
13
+ onGithub?: () => void
14
+ onGoogle?: () => void
15
+ }
16
+
17
+ const GitHubIcon = () => (
18
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor">
19
+ <path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z" />
20
+ </svg>
21
+ )
22
+
23
+ const GoogleIcon = () => (
24
+ <svg width="16" height="16" viewBox="0 0 24 24">
25
+ <path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" />
26
+ <path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" />
27
+ <path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" />
28
+ <path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" />
29
+ </svg>
30
+ )
31
+
32
+ const INPUT_CLS = "h-9 w-full rounded-lg border border-border bg-surface-2 px-3 py-2 text-sm outline-none transition-colors placeholder:text-muted-foreground focus-visible:ring-1 focus-visible:ring-border-strong focus-visible:ring-offset-1 focus-visible:ring-offset-background"
33
+
34
+ export function AuthPanel({
35
+ defaultMode = "login",
36
+ onLogin,
37
+ onRegister,
38
+ onGithub,
39
+ onGoogle,
40
+ }: AuthPanelProps) {
41
+ const [mode, setMode] = React.useState<AuthMode>(defaultMode)
42
+ const [email, setEmail] = React.useState("")
43
+ const [password, setPassword] = React.useState("")
44
+ const [name, setName] = React.useState("")
45
+ const [showPassword, setShowPassword] = React.useState(false)
46
+ const [loading, setLoading] = React.useState(false)
47
+
48
+ async function handleSubmit(e: React.FormEvent) {
49
+ e.preventDefault()
50
+ setLoading(true)
51
+ try {
52
+ if (mode === "login") {
53
+ await onLogin?.(email, password)
54
+ } else {
55
+ await onRegister?.(email, password, name)
56
+ }
57
+ } finally {
58
+ setLoading(false)
59
+ }
60
+ }
61
+
62
+ return (
63
+ <div className="w-full max-w-sm rounded-2xl border border-border bg-card p-8">
64
+ <div className="mb-6">
65
+ <h1 className="text-xl font-semibold tracking-tight">
66
+ {mode === "login" ? "Sign in" : "Create account"}
67
+ </h1>
68
+ <p className="text-sm text-muted-foreground mt-1">
69
+ {mode === "login"
70
+ ? "Welcome back. Enter your credentials to continue."
71
+ : "Get started in seconds. No credit card required."}
72
+ </p>
73
+ </div>
74
+
75
+ <div className="flex gap-2 mb-6">
76
+ {onGithub && (
77
+ <button
78
+ type="button"
79
+ onClick={onGithub}
80
+ className="flex flex-1 items-center justify-center gap-2 rounded-lg border border-border bg-surface-2 px-3 py-2 text-sm font-medium transition-colors hover:bg-surface-3 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border-strong"
81
+ >
82
+ <GitHubIcon />
83
+ GitHub
84
+ </button>
85
+ )}
86
+ {onGoogle && (
87
+ <button
88
+ type="button"
89
+ onClick={onGoogle}
90
+ className="flex flex-1 items-center justify-center gap-2 rounded-lg border border-border bg-surface-2 px-3 py-2 text-sm font-medium transition-colors hover:bg-surface-3 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border-strong"
91
+ >
92
+ <GoogleIcon />
93
+ Google
94
+ </button>
95
+ )}
96
+ </div>
97
+
98
+ {(onGithub || onGoogle) && (
99
+ <div className="relative flex items-center justify-center mb-6">
100
+ <div className="absolute inset-0 flex items-center">
101
+ <div className="w-full border-t border-border" />
102
+ </div>
103
+ <span className="relative bg-card px-3 text-xs text-muted-foreground">or continue with email</span>
104
+ </div>
105
+ )}
106
+
107
+ <form onSubmit={handleSubmit} className="space-y-4">
108
+ {mode === "register" && (
109
+ <div className="space-y-1.5">
110
+ <label className="text-xs font-medium text-foreground">Name</label>
111
+ <input
112
+ type="text"
113
+ placeholder="Your name"
114
+ value={name}
115
+ onChange={(e) => setName(e.target.value)}
116
+ required
117
+ autoComplete="name"
118
+ className={INPUT_CLS}
119
+ />
120
+ </div>
121
+ )}
122
+
123
+ <div className="space-y-1.5">
124
+ <label className="text-xs font-medium text-foreground">Email</label>
125
+ <input
126
+ type="email"
127
+ placeholder="you@example.com"
128
+ value={email}
129
+ onChange={(e) => setEmail(e.target.value)}
130
+ required
131
+ autoComplete="email"
132
+ className={INPUT_CLS}
133
+ />
134
+ </div>
135
+
136
+ <div className="space-y-1.5">
137
+ <div className="flex items-center justify-between">
138
+ <label className="text-xs font-medium text-foreground">Password</label>
139
+ {mode === "login" && (
140
+ <button type="button" className="text-xs text-muted-foreground hover:text-foreground transition-colors">
141
+ Forgot password?
142
+ </button>
143
+ )}
144
+ </div>
145
+ <div className="relative">
146
+ <input
147
+ type={showPassword ? "text" : "password"}
148
+ placeholder="••••••••"
149
+ value={password}
150
+ onChange={(e) => setPassword(e.target.value)}
151
+ required
152
+ minLength={8}
153
+ autoComplete={mode === "login" ? "current-password" : "new-password"}
154
+ className={cx(INPUT_CLS, "pr-9")}
155
+ />
156
+ <button
157
+ type="button"
158
+ onClick={() => setShowPassword(!showPassword)}
159
+ className="absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground transition-colors"
160
+ aria-label={showPassword ? "Hide password" : "Show password"}
161
+ >
162
+ {showPassword ? <EyeOff className="h-3.5 w-3.5" /> : <Eye className="h-3.5 w-3.5" />}
163
+ </button>
164
+ </div>
165
+ </div>
166
+
167
+ <button
168
+ type="submit"
169
+ disabled={loading}
170
+ className="w-full rounded-lg bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border-strong focus-visible:ring-offset-1 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50"
171
+ >
172
+ {loading ? "Loading…" : mode === "login" ? "Sign in" : "Create account"}
173
+ </button>
174
+ </form>
175
+
176
+ <p className="mt-5 text-center text-xs text-muted-foreground">
177
+ {mode === "login" ? (
178
+ <>
179
+ Don&apos;t have an account?{" "}
180
+ <button
181
+ type="button"
182
+ onClick={() => setMode("register")}
183
+ className="font-medium text-foreground hover:underline underline-offset-2"
184
+ >
185
+ Sign up
186
+ </button>
187
+ </>
188
+ ) : (
189
+ <>
190
+ Already have an account?{" "}
191
+ <button
192
+ type="button"
193
+ onClick={() => setMode("login")}
194
+ className="font-medium text-foreground hover:underline underline-offset-2"
195
+ >
196
+ Sign in
197
+ </button>
198
+ </>
199
+ )}
200
+ </p>
201
+ </div>
202
+ )
203
+ }
@@ -0,0 +1,122 @@
1
+ import * as React from "react"
2
+ import { cx } from "@/lib/cx"
3
+
4
+ export interface Feature {
5
+ icon: React.ReactNode
6
+ title: string
7
+ description: string
8
+ }
9
+
10
+ export interface FeatureGridProps {
11
+ eyebrow?: string
12
+ headline?: string
13
+ subtext?: string
14
+ features?: Feature[]
15
+ columns?: 2 | 3 | 4
16
+ className?: string
17
+ }
18
+
19
+ const defaultFeatures: Feature[] = [
20
+ {
21
+ icon: (
22
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
23
+ <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" />
24
+ </svg>
25
+ ),
26
+ title: "Instant Install",
27
+ description: "One CLI command stamps any block directly into your project. No config, no setup, no runtime dependency.",
28
+ },
29
+ {
30
+ icon: (
31
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
32
+ <rect width="18" height="18" x="3" y="3" rx="2" />
33
+ <path d="M3 9h18M9 21V9" />
34
+ </svg>
35
+ ),
36
+ title: "Full Source Ownership",
37
+ description: "The code lands in your repo. No dependency to maintain, no upstream surprises. Customize it freely.",
38
+ },
39
+ {
40
+ icon: (
41
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
42
+ <circle cx="12" cy="12" r="3" />
43
+ <path d="M12 2v3M12 19v3M4.22 4.22l2.12 2.12M17.66 17.66l2.12 2.12M2 12h3M19 12h3M4.22 19.78l2.12-2.12M17.66 6.34l2.12-2.12" />
44
+ </svg>
45
+ ),
46
+ title: "AI-Ready Prompts",
47
+ description: "Every block ships with optimized prompts for Cursor, Claude, v0, and Bolt. Describe what you want, get it built.",
48
+ },
49
+ {
50
+ icon: (
51
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
52
+ <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
53
+ </svg>
54
+ ),
55
+ title: "Accessible by Default",
56
+ description: "Built on Radix UI primitives. Keyboard navigation, ARIA attributes, and screen reader support out of the box.",
57
+ },
58
+ {
59
+ icon: (
60
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
61
+ <circle cx="12" cy="8" r="6" />
62
+ <path d="M15.477 12.89 17 22l-5-3-5 3 1.523-9.11" />
63
+ </svg>
64
+ ),
65
+ title: "Dark Mode Native",
66
+ description: "Every component is built with CSS variables and supports dark mode out of the box. No extra config.",
67
+ },
68
+ {
69
+ icon: (
70
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round">
71
+ <path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z" />
72
+ </svg>
73
+ ),
74
+ title: "Themeable",
75
+ description: "Token-based design system. Change one CSS variable, every component updates. Consistent without effort.",
76
+ },
77
+ ]
78
+
79
+ const colClass: Record<2 | 3 | 4, string> = {
80
+ 2: "sm:grid-cols-2",
81
+ 3: "sm:grid-cols-2 lg:grid-cols-3",
82
+ 4: "sm:grid-cols-2 lg:grid-cols-4",
83
+ }
84
+
85
+ export function FeatureGrid({
86
+ eyebrow = "Why StampUI",
87
+ headline = "Built for how you actually ship",
88
+ subtext,
89
+ features = defaultFeatures,
90
+ columns = 3,
91
+ className,
92
+ }: FeatureGridProps) {
93
+ return (
94
+ <section className={cx("w-full py-24 px-6", className)}>
95
+ <div className="mx-auto max-w-5xl">
96
+ <div className="mb-16 text-center">
97
+ {eyebrow && (
98
+ <p className="text-xs font-mono text-muted-foreground uppercase tracking-widest mb-4">{eyebrow}</p>
99
+ )}
100
+ <h2 className="text-3xl sm:text-4xl font-bold tracking-tight text-foreground">{headline}</h2>
101
+ {subtext && (
102
+ <p className="mt-4 text-base text-muted-foreground max-w-xl mx-auto">{subtext}</p>
103
+ )}
104
+ </div>
105
+
106
+ <div className={cx("grid grid-cols-1 gap-8", colClass[columns])}>
107
+ {features.map((feature, i) => (
108
+ <div key={i} className="flex flex-col gap-4">
109
+ <div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg border border-border bg-surface-2 text-foreground">
110
+ {feature.icon}
111
+ </div>
112
+ <div>
113
+ <h3 className="text-sm font-semibold text-foreground mb-1.5">{feature.title}</h3>
114
+ <p className="text-sm text-muted-foreground leading-relaxed">{feature.description}</p>
115
+ </div>
116
+ </div>
117
+ ))}
118
+ </div>
119
+ </div>
120
+ </section>
121
+ )
122
+ }
@@ -0,0 +1,73 @@
1
+ import * as React from "react"
2
+ import { Badge } from "@/components/core/badge"
3
+ import { Button } from "@/components/core/button"
4
+ import { cx } from "@/lib/cx"
5
+
6
+ export interface HeroSectionProps {
7
+ eyebrow?: string
8
+ headline?: string
9
+ subtext?: string
10
+ primaryCta?: { label: string; href?: string; onClick?: () => void }
11
+ secondaryCta?: { label: string; href?: string; onClick?: () => void }
12
+ align?: "left" | "center"
13
+ className?: string
14
+ }
15
+
16
+ export function HeroSection({
17
+ eyebrow = "Now in public beta",
18
+ headline = "Your app works.\nNow make it look shipped.",
19
+ subtext = "Production-ready UI blocks stamped directly into your project. No runtime, no lock-in — just clean code you own.",
20
+ primaryCta = { label: "Browse Blocks", href: "/blocks" },
21
+ secondaryCta = { label: "View Components", href: "/blocks/components" },
22
+ align = "center",
23
+ className,
24
+ }: HeroSectionProps) {
25
+ return (
26
+ <section
27
+ className={cx(
28
+ "w-full py-24 px-6",
29
+ align === "center" && "flex flex-col items-center text-center",
30
+ align === "left" && "flex flex-col items-start text-left max-w-2xl",
31
+ className
32
+ )}
33
+ >
34
+ {eyebrow && (
35
+ <Badge variant="neutral" className="mb-6 font-mono text-xs tracking-wider">
36
+ {eyebrow}
37
+ </Badge>
38
+ )}
39
+
40
+ <h1 className="text-4xl sm:text-5xl lg:text-6xl font-bold tracking-tight text-foreground leading-tight whitespace-pre-line max-w-3xl">
41
+ {headline}
42
+ </h1>
43
+
44
+ {subtext && (
45
+ <p className="mt-6 text-base sm:text-lg text-muted-foreground max-w-xl leading-relaxed">
46
+ {subtext}
47
+ </p>
48
+ )}
49
+
50
+ <div className={cx("mt-10 flex flex-wrap gap-3", align === "center" && "justify-center")}>
51
+ {primaryCta && (
52
+ <Button
53
+ asChild={!!primaryCta.href}
54
+ onClick={primaryCta.onClick}
55
+ size="lg"
56
+ >
57
+ {primaryCta.href ? <a href={primaryCta.href}>{primaryCta.label}</a> : <span>{primaryCta.label}</span>}
58
+ </Button>
59
+ )}
60
+ {secondaryCta && (
61
+ <Button
62
+ asChild={!!secondaryCta.href}
63
+ onClick={secondaryCta.onClick}
64
+ variant="outline"
65
+ size="lg"
66
+ >
67
+ {secondaryCta.href ? <a href={secondaryCta.href}>{secondaryCta.label}</a> : <span>{secondaryCta.label}</span>}
68
+ </Button>
69
+ )}
70
+ </div>
71
+ </section>
72
+ )
73
+ }